std::thread

1,头文件

#include <thread>

2,调用thread类创建线程(右击--转到定义,可见thread上一个类),同时直接传递线程函数或者创建线程的类对象。

线程的创建方式 

函数创建线程

不需要线程对象的创建方法

    void handle(){
        std::thread{std::bind(&ServiceClient_01::sendRequest, this)}.detach();
    }

这行代码做了以下几件事:
a. 创建了一个临时的 std::thread 对象。
b. 这个对象在构造时,操作系统底层真正创建了一个新的执行线程
c. 这个新线程开始独立执行 sendRequest 函数。
d. 此时,临时 std::thread 对象是这个新执行线程的"句柄"或"管理者"。

e. 将执行线程与它的管理对象(std::thread 对象)分离。

std::thread创建的是一个临时对象,临时对象所在的行执行结束之后,临时对象就被释放了,但是启动线程,也就是sendRequest为什么还可以继续执行?

线程(创建)对象和线程

核心答案:线程对象(std::thread)和线程本身(执行流)是两个完全不同的东西。线程对象的销毁并不等于它管理的执行线程的终止。

std::thread{std::bind(&ServiceClient_01::sendRequest, this)}.detach();

detach() 的关键作用是:将执行线程与它的管理对象(std::thread 对象)分离

分离后:

  • 执行线程:变成"野生的"或"后台的",它继续独立运行,直到 sendRequest 函数执行完毕自然退出。

  • std::thread 对象:变成"空的"或"非线程"状态,它不再关联任何执行线程。

3. 临时对象被销毁

分号 ; 结束,临时 std::thread 对象生命周期结束,调用析构函数。

  • 因为已经调用了 detach(),该对象现在是"不可联结"(not joinable)的状态。

  • 标准规定,析构一个不可联结的 std::thread 对象是安全的,什么也不会发生(no-op)。对象本身被销毁,释放了管理线程所用的少量资源(如线程id等)。

  • 至关重要的点:销毁的只是管理壳,而不是它曾经管理的那个执行线程


生动的比喻

把这个问题想象成放风筝

  1. std::thread{}:你手里拿着一个风筝线轴(线程对象),并把它扔到天上(创建执行线程)。

  2. .detach():你主动剪断了风筝线(分离)。现在风筝(执行线程)在天上自由飞翔,不再受线轴控制。

  3. 临时对象析构:你把 now useless 空线轴(线程对象)扔进垃圾桶(销毁)。

  4. 结果:风筝(sendRequest 线程)依然在天上飞,直到它自己没风了掉下来(函数执行完毕)。你扔掉的只是线轴,不是风筝本身。

操作系统才是真正负责管理和调度所有线程(风筝)的"天空"。只要线程的入口函数(sendRequest)还没执行完,操作系统就会继续让它运行,根本不在乎当初创建它的那个C++对象是否还存在。


如果没有 detach() 会怎样?

作为对比,如果代码是 std::thread{...};(没有调用 detach()join()):

  1. 临时线程对象被销毁。

  2. 在析构时,它发现自已还关联着一个活的线程(是可联结状态)。

  3. 标准规定,析构一个可联结的线程对象是严重错误,会导致程序调用 std::terminate() 直接终止。

这就是为什么必须在线程对象销毁前调用 join()(等待它结束)或 detach()(放它自由)。

-- 线程对象和线程是即可以关联又可以独立两个对象。

类/结构体对象创建线程

struct fang {
	void operator()() {
		cout << "create thread..." << endl;
	}
};

int main() {
	fang threadF;
	thread func(threadF);
	if(func.joinable())
		func.detach();

	return 0;
 }

因为lambda返回的是函数指针,所以也可以

lambda函数创建线程

thread thread1([] {
		int a = 100, b = 122;
		cout << a + b << endl;
		});
	thread1.join();

参数传递创建线程

注意:多个参数都可以传递

void add(int a, int b) {
	a++;
	cout << a + b;
}
int main() {
	int a = 10, b = 2;
	thread thread2(add,a,b);
	thread2.join();

	return 0;
 }

指针传递创建线程

void add(int* a, int* b) {
	(*a)++;
	cout << *a + *b;
}
int main() {
	int a = 10, b = 2;
	thread thread2(add,&a,&b);
	thread2.join();

	return 0;
 }

引用参数传递创建线程

注意:如果参数是引用,需要用std::ref函数包装才可以传递

void add(int& a, int& b) {
	a++;
	cout << a + b;
}
int main() {
	int a = 10, b = 2;
	thread thread2(add,std::ref(a),std::ref(b));
	thread2.join();

	return 0;
 }

有智能指针作为参数的线程函数

智能指针要用move转为右值

void add3(unique_ptr<int> ptr) {
	cout << "子线程:" << ptr.get() << endl;
}
int main() {
	unique_ptr<int> ptr(new int(100));
	cout << "主线程:" << ptr.get() << endl;
	thread thread3(add3,move(ptr));
	thread3.join();
	cout << "指针对象move之后不存在:" << ptr.get() << endl;

	return 0;
 }

类成员函数创建线程

使用类成员函数创建的线程,不同线程可以共享一个类的成员对象吗?

#include <iostream>
#include <thread>
#include <unistd.h>

class Test{
public:
    void run(int v){
      setValue(v);
      sleep(5);
      std::cout<<"sub thread value="<<value<<std::endl;
    }
    void setValue(int v){
      value=v;
    }
    int getValue(){
      return value;
    }

private:
    int value{10};
};

Test t2;

int main(){
    Test t1;
    std::thread sub_thread(&Test::run,t1,100);
    sub_thread.join();
    std::cout<<"main thread1 vlaue="<<t1.getValue()<<std::endl;

    std::thread sub_thread2(&Test::run,t2,100);
    sub_thread2.join();
    std::cout<<"main thread2 vlaue="<<t2.getValue()<<std::endl;

    return 0;
}

结果表明无论类对象是全局还是局部对象子线程和主线程并不共享同一个类的成员和成员函数。

原因:

这段代码中,类对象t1是参数,参数的传递本质上是值拷贝。指定t1为子线程的参数,那么创建子线程的时候就会创建一个临时的Test类对象,只是这个类对象拷贝t1对象的数据而已,所以子线程中操作的是临时对象,而不是t1对象。

如何让子线程和主线程操作同一个类对象?

解决办法:

t1对象指定为指针传递或者引用传递即可。

#include <iostream>
#include <thread>
#include <unistd.h>

class Test{
public:
    void run(int v){
      //setValue(v);
      sleep(2);
      std::cout<<"sub thread value="<<getValue()<<std::endl;
    }
    void setValue(int v){
      value=v;
    }
    int getValue(){
      return value;
    }

private:
    int value{10};
};

int main(){
    Test t1;
    t1.setValue(111);
    std::thread sub_thread(&Test::run,&t1,100); //指针
    sub_thread.join();
    std::cout<<"main thread1 vlaue="<<t1.getValue()<<std::endl;
    return 0;
}

在类的函数中开启另一个线程对类进行操作
#include <iostream>
#include <thread>
#include <unistd.h>

class Test{
public:
    void init(){
      std::thread th(&Test::run,this,111);
      th.join();
      std::cout<<"main address="<<this<<std::endl;
      std::cout<<"main thread1 vlaue="<<getValue()<<std::endl;
    }
    void run(int v){
      setValue(v);
      sleep(2);
      std::cout<<"sub address="<<this<<std::endl;
      std::cout<<"sub thread value="<<getValue()<<std::endl;
    }
    void setValue(int v){
      value=v;
    }
    int getValue(){
      return value;
    }

private:
    int value{10};
};

int main(){
    Test t1;
    t1.init();

    return 0;
}

相同的地址,相同的值。

vector容器的emplace_back()函数创建线程

vector<thread> vecThread;
	int a = 10, b = 23;
	for (int i = 0; i < 5; ++i) {
		vecThread.emplace_back(add, a, b);
		vecThread[i].join();
	}

joint和detach

joint和detach可以都调用吗

不可以。

#include <iostream>
#include <thread>
#include <unistd.h>

class Test{
public:
    void init(){
      std::thread th(&Test::run,this);
      th.detach();
      int time=0;
      std::cout<<"main address="<<this<<std::endl;
      while(run_flag_){
        time++;
        if(time==5) run_flag_=false;
        sleep(1);
      }
      std::cout<<"main thread1 vlaue="<<getValue()<<std::endl;
      th.join();//执行到这的时候报错--system_error
    }
    void run(){
      std::cout<<"sub address="<<this<<std::endl;
      while(run_flag_){
          setValue();
          std::cout<<"sub thread value="<<getValue()<<std::endl;
          sleep(1);
      }
    }
    void setValue(){
      value++;
    }
    int getValue(){
      return value;
    }

private:
    int value{10};
    bool run_flag_{true};
};

int main(){
    Test t1;
    t1.init();

    return 0;
}

原因:

detach已经分离了主线程和子线程的依赖关系,后续joint就joint不到了。

join和detach的调用方式

以下方式是可以的:

第一种方式:匿名变量。

std::thread{std::bind(&ServiceClient_01::sendRequest, this)}.detach();
std::thread{std::bind(&ServiceClient_01::sendRequest, this)}.join();

std::thread th([](){...});
th.detach();
//th.join()
   

以下方式不服和也语法规则:

std::thread th([](){}).detach();
std::thread th([](){}).join();

std::thread不指定detach和join

互斥锁

头文件

#include <mutex>

不同于linux,这个mutex创建对象之后就可以使用,不需要初始化。

线程同步是为了有序操作公共资源或者保护公共资源

try_lock()

尝试加锁,如果锁已经被锁,返回false,所以try_lock()需要对是否上锁成功做判断

条件变量--condition_varieble

wait(unique_lock<mutex>)

阻塞当前线程直到条件变量被唤醒;

unqiue_lock<mutex>

作用:自动或者调用lock获取并上锁,作用域结束时自动释放互斥锁;

unique_ lock提供了以下几种加锁和解锁方式: .
1.默认构造函数创建一个未加锁的unique_ lock对象, 可以通过调用其成员函数lock()来手动加锁,通过unlock()来手动解锁。
2.使用构造函数传入互斥量,并加锁。
3.使用构造函数传入互乐量,但不自动加锁,可以通过调用其成员函数try_ _lock()来尝 试加锁,如果成功返回true,失败返回false。
4.可以通过传入st.:adopt_ lock参 数来构造unique_ lock对象, 表示对已经加锁的互斥进行管理。
 

unique_lock和lock_guard都是管理锁的辅助类工具,都是RAII风格;它们是在定义时获得锁,在析构时释放锁。

notify_all()

作用:唤醒所有等待这个条件变量的线程;

notify_one()

作用:唤醒一个等待这个条件变量的线程;

链接

三个线程A、B、C分别打印1-100,A打印3的倍数,B打印5的倍数,C打印其他数。不能重复

#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
using namespace std;

mutex m_mutex;
condition_variable cond;
int n = 1;

void printA() {
    while (n <= 100) {
        unique_lock<mutex> lck(m_mutex);
        if (n % 3 == 0) {
            cout<<"A:" << n << endl;
            n++;
            cond.notify_all();
        }
        else {
            cond.wait(lck);
        }
    }
}
void printC() {
    while (n <= 100) {
        unique_lock<mutex> lck(m_mutex);
        if (n % 3 != 0 || n % 5 != 0) {
            cout <<"C:" << n << endl;
            n++;
            cond.notify_all();
        }
        else {
            cond.wait(lck);
        }
    }
}
void printB() {
    while (n <= 100) {
        unique_lock<mutex> lck(m_mutex);
        if (n % 5 == 0) {
            cout <<"B:" << n << endl;
            n++;
            cond.notify_all();
        }
        else {
            cond.wait(lck);
        }
    }
}

int main() {
    thread t1(printA),t2(printB),t3(printC);
    t1.join();
    t2.join();
    t3.join();

    return 0;
}

三个线程A,B,C,循环按序输出1,2,3一直输出到100

方法1:使用一个条件变量,,三个bool标志

#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
using namespace std;

mutex m_mutex;
condition_variable cond;
bool p1 = false , p2 = false, p3 = false;
int n = 0;

void printA() {
    while (n <= 100) {
        unique_lock<mutex> lck(m_mutex);
        if (p1) {
            cout<<1<< endl;
            n++;
            p1 = false;
            p2 = true;
            cond.notify_all();
        }
        else {
            cond.wait(lck);
        }
    }
}
void printC() {
    while (n <= 100) {
        unique_lock<mutex> lck(m_mutex);
        if (p2) {
            cout <<2<< endl;
            n++;
            p2 = false;
            p3 = true;
            cond.notify_all();
        }
        else {
            cond.wait(lck);
        }
    }
}
void printB() {
    while (n <= 100) {
        unique_lock<mutex> lck(m_mutex);
        if (p3) {
            cout <<3<< endl;
            n++;
            p3 = false;
            p1 = true;
            cond.notify_all();
        }
        else {
            cond.wait(lck);
        }
    }
}

int main() {
    p1 = true;
    thread t1(printA),t2(printB),t3(printC);
    t1.join();
    t2.join();
    t3.join();

    return 0;
}

方法2:各自线程使用各自的条件变量,三个标志

#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
using namespace std;

mutex m_mutex;
condition_variable cond1,cond2,cond3;
bool p1 = false , p2 = false, p3 = false;
int n = 0;

void printA() {
    while (n <= 100) {
        unique_lock<mutex> lck(m_mutex);
        if (p1) {
            cout<<1<< endl;
            n++;
            p1 = false;
            p2 = true;
            cond2.notify_all();//唤醒线程2
        }
        else {
            cond1.wait(lck);//线程1等待
        }
    }
}
void printC() {
    while (n <= 100) {
        unique_lock<mutex> lck(m_mutex);
        if (p2) {
            cout <<2<< endl;
            n++;
            p2 = false;
            p3 = true;
            cond3.notify_all();
        }
        else {
            cond2.wait(lck);
        }
    }
}
void printB() {
    while (n <= 100) {
        unique_lock<mutex> lck(m_mutex);
        if (p3) {
            cout <<3<< endl;
            n++;
            p3 = false;
            p1 = true;
            cond1.notify_all();
        }
        else {
            cond3.wait(lck);
        }
    }
}

int main() {
    p1 = true;
    thread t1(printA),t2(printB),t3(printC);
    t1.join();
    t2.join();
    t3.join();

    return 0;
}

多进程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿饵块

有帮助就给点辛苦费吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值