• C++11 多线程


    1、创建线程

    方式一:

    #include 
    #include 
    #include 
    using namespace std;
    
    // 自己创建的线程需要从一个函数开始允许
    void myThread()
    {
    	cout << "myThread执行完成" << endl;
    }
    
    int main()
    {
    	// myThread可调用对象
    	//创建线程,开始执行
    	thread myObj(myThread);
    	//阻塞主线程,等待子线程执行完毕,
    	myObj.join();
    
    	// 线程分离,主线程不等待子线程完毕
    	//myObj.detach();
    	// joinable:判断是否可以调用join或detath,true(可以),false(不可以)
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    方式二:

    class TA
    {
    public:
    	TA()
    	{
    		cout << "构造函数" << endl;
    	}
    	~TA()
    	{
    		cout << "~析构函数" << endl;
    	}
    	TA(const TA& tmp)
    	{
    		cout << "拷贝构造函数" << endl;
    	}
    	void operator()()	// 可调用对象
    	{
    		// 线程入口点
    		cout << "TA执行完成" << endl;
    	}
    };
    
    
    TA ta;
    thread myObj(ta);
    myObj.join();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    方式三:

    auto my = [] {
    	cout << "lambda表达式创建线程" << endl;
    };
    thread myObj(my);
    myObj.join();
    
    • 1
    • 2
    • 3
    • 4
    • 5

    需要传入的是一个可调用对象,总的来说,可调用对象可以是以下几种情况:
    普通函数
    函数指针
    仿函数,即重载了operator()运算符的类对象
    匿名函数,即Lambda表达式
    std::function

    1.普通函数
    void cmp() 
    {
    }
    
    2.函数指针
    // 定义一个无参返回值是void的函数指针
    typedef void (*CMPFUN)();
    // using 写法定义函数指针
    using CMPFUN = void(*)();
    CMPFUN p1 = cmp;
    
    3.仿函数(函数后const可加可不加,加了表示该函数不可以修改类属性)
    class A{
        void operator()() const{
            return;
        }
    };
    
    4、Lambda表达式
    auto my = [] {
    	cout << "lambda表达式创建线程" << endl;
    };
    
    5. std::function
    std::function在C++11后加入标准,可以用它来描述C++中所有可调用实体,它是是可调用对象的包装器,声明如下
    #include 
    
    // 声明一个返回值为int,参数为两个int的可调用对象类型
    std::function<int(int, int)> Func;
    // 普通函数
    int func_sum(int a, int b)
    {
        return a + b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    2、detach使用陷阱

    str指针和buf是同一个地址,所以detach后,主线程先退出,绝对会出现问题

    void func(const int& i, char* str)
    {
    	// 分析证明:i并不是num的引用,实际是值传递,即使主线程detach,子线程的i也是安全的
    	cout << i << endl;
    	cout << str << endl;
    }
    
    int num = 10;
    int& mynim = num;
    char buf[] = "hello world";
    thread myObj(func, num, buf);
    myObj.detach();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    修改后:

    通过将隐式类型转换,将char* 转换成string,但是这里隐藏有一个问题,就是可能主线程已经结束了,才去进行隐式类型转换, std::this_thread::get_id() 返回当前线程id

    void func(const int& i,const string& str)
    {
    	// 分析证明:i并不是num的引用,实际是值传递,即使主线程detach,子线程的i也是安全的
    	cout << i << endl;
    	cout << str << endl;
    }
    
    int num = 10;
    int& mynim = num;
    char buf[] = "hello world";
    thread myObj(func, num, buf);
    myObj.detach();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    通过一段代码验证这个猜想(主线程已经退出,之后在完成隐式转换)

    class A
    {
    public:
    	A(int n)
    	{
    		cout << "A的构造函数调用:" <<std::this_thread::get_id()<< endl;
    	}
    
    	~A()
    	{
    		cout << "A的析构函数调用:" << std::this_thread::get_id() << endl;
    	}
    	A(const A& tmp)
    	{
    		cout << "A的拷贝构造函数调用:" << std::this_thread::get_id() << endl;
    	}
    
    };
    
    void myfun(const A& tmp)
    {
    	cout << "子线程执行完成:" << std::this_thread::get_id() << endl;
    }
    
    int num = 1;
    thread myObj(myfun,num);
    myObj.join();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述

    通过控制台可以看出,既然是在子线程中构造的A对象。

    修改完整版:

    // 只需要修改传入的参数,在主线程中构造一个临时对象
    int num = 1;
    thread myObj(myfun,A(num));
    myObj.detach();
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    3、引用说明

    通过上面代码可以看到,我们给线程传递参数虽然使用的是引用传递,但是其实还是值传递,如果我们想要修改传入对象的属性怎么修改?使用std::ref()函数

    class A
    {
    public:
    	A(int n):m_n(n)
    	{
    		cout << "A的构造函数调用:" <<std::this_thread::get_id()<< endl;
    	}
    
    	~A()
    	{
    		cout << "A的析构函数调用:" << std::this_thread::get_id() << endl;
    	}
    	A(const A& tmp)
    	{
    		cout << "A的拷贝构造函数调用:" << std::this_thread::get_id() << endl;
    	}
    	int m_n;
    };
    
    void myfun(A& tmp)
    {
    	tmp.m_n = 199;
    	cout << "子线程执行完成:" << std::this_thread::get_id() << endl;
    }
    
    A a(10);
    thread myObj(myfun,std::ref(a));
    myObj.join();
    cout << a.m_n << endl;
    cout << "主线程执行完成:" << std::this_thread::get_id()<<endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    4、传递智能指针

    void myfun2(unique_ptr<int> p)
    {
    	cout << *p << endl;
    	cout << "子线程执行完成:" << std::this_thread::get_id() << endl;
    }
    
    unique_ptr<int> up(new int(100));
    thread myObj(myfun2,std::move(up));
    myObj.join();	// 这里一定不能使用detach,因为要是使用可能主线程已经退出,然后up已经释放了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5、成员函数做线程参数

    class A
    {
    public:
    	A(int n):m_n(n)
    	{
    		cout << "A的构造函数调用:" <<std::this_thread::get_id()<< endl;
    	}
    
    	~A()
    	{
    		cout << "A的析构函数调用:" << std::this_thread::get_id() << endl;
    	}
    	A(const A& tmp)
    	{
    		cout << "A的拷贝构造函数调用:" << std::this_thread::get_id() << endl;
    	}
    	void thread_work(int num)
    	{
    		cout << "num:" << num << endl;
    	}
    	int m_n;
    };
    
    A a(10);
    thread myObj(&(A::thread_work), &a, 10); // 这个地方用引用,使用a
    myObj.join();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    6、创建和等待多个线程

    #include 
    #include 
    #include 
    #include 
    using namespace std;
     void myfun3(int num)
    {
    	cout << "子线程执行完成:" << std::this_thread::get_id() <<"num:" << num << endl;
    }
    
    int main()
    {
    	vector<thread> vThread;
    	for (int i = 0; i < 10; i++)
    	{
    		vThread.push_back(thread(myfun3, i));
    	}
    
    	for (auto iter = vThread.begin(); iter != vThread.end(); iter++)
    	{
    		iter->join();
    	}
    	cout << "主线程执行结束" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    7、数据共享问题

    有读有写时,对共享数据不做处理会出现异常情况

    class Queue
    {
    public:
    	void inMsgQueue()
    	{
    		for (int i = 0; i < 100000; i++)
    		{
    			cout << "插入数字:" << i <<" " << std::this_thread::get_id() << endl;
    			m_lQueue.push_back(i);
    		}
    	}
    
    	void outMsgQueue()
    	{
    		for (int i = 0; i < 100000; i++)
    		{
    			if (!m_lQueue.empty())
    			{
    				cout << m_lQueue.front()<<endl;
    				m_lQueue.pop_front();
    			}
    			else
    			{
    				cout << "队列中没有数据" << endl;
    			}
    		}
    	}
    
    private:
    	list<int> m_lQueue;
    };
    
    
    Queue que;
    thread inMsg(&Queue::inMsgQueue, &que);	// 如果不传入引用,会调用拷贝构造函数,操作的是临时对象
    thread outMsg(&Queue::outMsgQueue, &que);
    
    inMsg.join();
    outMsg.join();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    8、互斥量

    保护共享数据,操作时,某个线程通过代码进行加锁然后操作数据,解锁
    其他线程想操作数据必须等待解锁,然后同样加锁,操作数据,解锁

    互斥量(mutex),是一个类对象,也可以理解为一把锁,多个尝试用lock成员函数来加锁,只有一个线程会成功

    class Queue
    {
    public:
    	void inMsgQueue()
    	{
    		for (int i = 0; i < 100000; i++)
    		{
    			cout << "插入数字:" << i <<" " << std::this_thread::get_id() << endl;
    			m_mutex.lock();
    			m_lQueue.push_back(i);
    			m_mutex.unlock();
    		}
    	}
    
    	void outMsgQueue()
    	{
    		for (int i = 0; i < 100000; i++)
    		{
    			m_mutex.lock();
    			if (!m_lQueue.empty())
    			{
    				cout <<"输出:" << m_lQueue.front() << endl;	
    				m_lQueue.pop_front();
    			}
    			else
    			{
    				cout << "空" << endl;
    			}
    			m_mutex.unlock();
    		}
    	}
    
    private:
    	list<int> m_lQueue;
    	mutex	m_mutex;	// 创建一个互斥量
    };
    
    int main()
    {
    	Queue que;
    	thread outMsg(&Queue::outMsgQueue, &que);
    	thread inMsg(&Queue::inMsgQueue, &que);
    	
    
    	inMsg.join();
    	outMsg.join();
    
    	cout << "主线程执行结束" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    std::lock_guard替代lock和unlock

    // lock_guard在构造函数中执行了lock,在析构函数中执行了unlock,所以可以替代
    std::lock_guard<mutex> sbguard(m_mutex);
    
    • 1
    • 2

    9、死锁

    比如我有两把锁,(死锁的问题,至少需要两把锁也就是两个互斥量才会产生)
    锁1、锁2
    线程1执行的时候,先锁1,然后去锁2。。。
    线程2执行的时候,先锁2,然后锁1
    此时,死锁产生

    std::lock(my_mutex1,my_mutex2);	// 相当于每个互斥量都调用.lock
    std::lock_guard<mutex> sb1(my_mutex1,std::adopt_lock);
    std::lock_guard<mutex> sb1(my_mutex2,std::adopt_lock);	// 表示已经lock这里不会lock了,结束时调用unlock
    
    • 1
    • 2
    • 3

    10、unique_lock

    unique_lock取代lock_guard,unique_lock是一个类模板,比lock_guard更方便,但是效率要低一些

    std::unique_lock<mutex> unilock(m_mutex);
    
    • 1

    unique_lock第二个参数

    1、std::adopt_lock	// 表示互斥量已经lock了,不需要在构造函数中lock了
    
    2、std::try_to_lock // 去锁定这个,但如果没有锁定成功,则立即返回,并不会阻塞,使用之前不能lock
    // 因为他会去尝试lock
    
    3、std::defer_lock // 不能自己先lock,否则报异常,初始化一个没有加锁的mutex,
    
    
    // 可以通过owns_lock()来进行判断是否拿锁成功
    if(unilock.owns_lock())
    {
    	cout<<"成功"<<endl;
    }
    else
    {
    	cout<<"不成功"<<endl;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    unique_lock的成员函数

    // 创建了一个没有加锁的mutex
    std::unique_lock<mutex> unilock(m_mutex,std::defer_lock);
    unilock.lock();		// 调用了unique_lock的成员函数
    unilock.unlock();	
    unilock.try_lock();	// 不阻塞,尝试加锁,如果拿不到锁返回false,否则返回true
    unilock.release();	// 返回锁管理的mutex对象指针,并释放所有权,unique_mutex不在和他有关系
    // 如果原来mutex处于加锁,调用release之后,要负责解锁
    std::mutex* ptex = unilock.release();	// ptex指向m_mutex
    ptex->unlock();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    线程休息

    std::chrono::milliseconds dura(2000);	// 1秒=1000毫秒,
    std::this_thread::sleep_for(dura);	// 休息一定时间
    
    • 1
    • 2

    11、单例设计模式

    单例设计模式,使用频率高,
    单例:整个项目中,有某个或者某些特殊的类,属于该类对象,我只能创建1个,多了创建不了

    单例写法

    std::mutex mymutex;
    class A
    {
    private:
    	A()	// 私有化构造函数
    	{
    	}
    private:
    	static A* m_instance;	// 静态成员变量
    public:
    	static A* GetInstance()
    	{
    		if(m_instance==NULL)
    		{
    			m_instance = new A();
    			static Ahuishou ahui1;	// 静态变量在程序退出时会释放,所以会调用析构函数
    		}
    		return m_instance;
    	
    	}
    	// 类中套类,用来回收释放对象
    	class Ahuishou
    	{
    	public:
    		~Ahuishou()
    		{
    			if(A::m_instance!=NULL)
    			{
    				delete A::m_instance;
    				A::m_instance = NULL;
    			}	
    		}
    	}
    };
    // 静态成员初始化
    A* A::m_instance = NULL;
    
    
    // 使用
    A* a = A::GetInstance(); 	// 创建一个对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    拓展:如果使用线程创建对象,就需要加锁

    std::mutex mymutex;
    class A
    {
    private:
    	A()	// 私有化构造函数
    	{
    	}
    private:
    	static A* m_instance;	// 静态成员变量
    public:
    	static A* GetInstance()
    	{
    		if(m_instance==NULL)
    		{
    			unique_mutex<mutex> myunique(mymutex);	//自动加锁解锁
    			if(m_instance==NULL)
    			{
    				m_instance = new A();
    				static Ahuishou ahui1;	// 静态变量在程序退出时会释放,所以会调用析构函数
    			}	
    		}
    		return m_instance;
    	}
    	// 类中套类,用来回收释放对象
    	class Ahuishou
    	{
    	public:
    		~Ahuishou()
    		{
    			if(A::m_instance!=NULL)
    			{
    				delete A::m_instance;
    				A::m_instance = NULL;
    			}	
    		}
    	}
    };
    // 静态成员初始化
    A* A::m_instance = NULL;
    
    
    // 使用
    //A* a = A::GetInstance(); 	// 创建一个对象
    void myfun()
    {
    	A* a = A::GetInstance(); 	// 创建一个对象
    }
    
    thread mythread(myfun);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    12、std::call_once()

    std::call_once() c++11引入函数,该函数的第二个参数是一个函数名,能够保证函数之被调用一次,具备互斥量能力,比互斥量销毁资源更少,是通过once_flag来控制的

    std::once_flag g_flag;
    std::call_once(g_flag,a());	// 两个线程同时执行到这个地方,第二个线程就需要等待,后面不会调用a函数
    
    • 1
    • 2

    13、条件变量

    std::condition_variable实际上是一个类,是一个和条件相关的一个类,说白了就是等待一个条件达成,这个类是需要和互斥量配合工作的,需要生产类对象

    std::condition_variable m_cond;	// 生成一个条件对象
    std::unique_lock<std::mutex> sbguard1(m_mutex);
    // wait用来等待一个东西
    // 如果第二个参数lambda表达式,返回true,那么wait直接返回
    // 第二个参数lambda表达式,返回值是false,那么wait将解锁互斥量,堵塞到本行
    // 堵塞到,其他线程调用notify_one(),成员函数为止
    // 如果wait没有第二个参数,那么就跟第二个参数lambda表达式返回false一样
    // wait被唤醒只会,就会不断请求互斥量锁,如果获取不到,一直获取,如果获取到,继续走
    // 如果wait有第二个表达式,wait继续重复上流程
    m_cond.wait(sbguard1,[this]{	// 一个lambda表达式,可调用对象
    	if(!m_queque.empty())		// 队列为空反回false
    		return true;
    	return false;		
    });
    
    m_cond.notify();	// 唤醒wait线程
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    notify_all()

    通知所有线程

    14、std::async、std::future创建后台任务并返回值

    std::async是一个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,他会返回std::future对象,std::future含有线程入口函数返回的结果,future对象的成员函数get来获取结果。
    std::launch类型(枚举类型),来达到一些特殊目的,std::launch::deferred表示线程入口函数调用被延迟到future对象的wait或者get函数调用时才执行(要是没有调用wait或者和get就不会执行线程,实际上没创建),std::launch::deferred延迟调用,并没有创建新线程,是在主线程中调用的线程入口函数。

    #include 
    
    int mythread()
    {
    	cout << "start threadid:" << std::this_thread::get_id() << endl;
    	std::chrono::milliseconds draw(5000);
    	std::this_thread::sleep_for(draw);		// 线程休息5000
    	cout << "end threadid:" <<std::this_thread::get_id()<< endl;
    	return 5;
    }
    
    int main()
    {
    
    	cout << "主线程:" << std::this_thread::get_id() << endl;
    	// std::future result = std::async(std::launch::deferred,mythread);	// 加入参数
    	std::future<int> result = std::async(mythread);
    	int i = 11;
    	i = 2;
    	cout << "111111111" << endl;
    	cout << result.get()<<endl;
    	cout << "主线程执行结束" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    15、std::packaged_task打包任务,把任务包起来

    是类模板,他的模板参数是,各种可调用对象,通过std::packaged_task来把各种可调用对象包装起来,方便将来将来作为线程入口函数调用,

    int mythread(int n)
    {
    	cout << "start threadid:" << std::this_thread::get_id() << endl;
    	std::chrono::milliseconds draw(5000);
    	std::this_thread::sleep_for(draw);		// 线程休息5000
    	cout << "end threadid:" <<std::this_thread::get_id()<< endl;
    	return 5;
    }
    
    int main()
    {
    	cout << "主线程:" << std::this_thread::get_id() << endl;
    	std::packaged_task<int(int)> mypt(mythread);	//	返回值int,一个int参数
    	// 使用std::ref可以在模板传参的时候传入引用
    	std::thread t1(std::ref(mypt),1);	// 线程直接开始执行
    	t1.join();
    	std::future<int> result = mypt.get_future();
    	cout<<result.get()<<endl;
    	cout << "主线程执行结束" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    16、std::promise类模板

    我们能够在某个线程中给它赋值,然后我们可以再其他线程中,把这个值取出来用

    void mythread1(std::promise<int>& tmp, int calc)	
    {
    	calc++;
    	calc *= 3;
    	std::chrono::milliseconds draw(5000);
    	std::this_thread::sleep_for(draw);		// 线程休息5000
    
    	int result = calc;
    	tmp.set_value(result);	// 结果我保存到tmp对象中
    }
    
    int main()
    {
    
    	cout << "主线程:" << std::this_thread::get_id() << endl;
    	std::promise<int> myprom;	// 声明一个promise对象,保存的值是int类型
    	std::thread t1(mythread1, std::ref(myprom), 180);
    	t1.join();
    	std::future<int> result = myprom.get_future();
    	cout << result.get();
    	cout << "主线程执行结束" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    17、std::future的其他成员函数

    // 是一个枚举类型
    std::future<int> result = std::async(mythread);
    
    // 等待1秒钟,希望你返回,如果没有返回,就表示超时
    std::future_status status =  result.wait_for(std::chrono::seconds(1));	// 等待1秒
    if(status ==std::future_status::timeout)
    {
    	// 超时,表示线程还没执行完,
    }
    else if(status ==std::future_status::ready)
    {
    	// 表示线程成功返回
    }
    else if(status ==std::future_status::deferred)
    {
    	// 如果async第一个参数设置为,std::launch::deferred,则条件成立
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    18、std::shared_future

    为什么第二次调用get时,会出现异常,是因为get的设计是一个移动语句,将里面的值移动出来,所以第二次调用里面没有东西了。

    std::shared_future的get函数是复制数据
    
    • 1

    19、原子操作std::atomic

    原子操作概念:可以理解为,无锁技术的并发,不需要互斥锁(加锁解锁),原子操作是多线程中,不会被打断的,程序执行片段。互斥量可以加锁代码段,原子操作一般操作一个变量的,原子操作指的是不可分割的操作,也就是说这种操作要么是完整的要么是不完整的,不可能出现半完成。

    std::atomic<int> g_mycount;	// 封装一个类型为int对象值
    
    void mythreadfun()
    {
    	for (int i = 0; i < 1000000; i++)
    	{
    		g_mycount++;
    	}
    }
    
    int main()
    {
    	thread my1(mythreadfun);
    	thread my2(mythreadfun);
    	my1.join();
    	my2.join();
    	cout << g_mycount << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    20、std::thread 和std::async区别

    std::thread系统资源紧张,创建线程会失败,std::async可能创建也可能不创建线程,很方便获取线程返回值

  • 相关阅读:
    springboot高校专业招生信息管理系统jsp001
    【计算机毕业设计】基于JSP的网上购物系统的设计与实现
    基于python-django协同过滤豆瓣电影推荐系统
    JS进阶-垃圾回收机制和算法
    B+树索引(9)之索引应用排序的注意事项
    4个有影响力的绩效管理最佳实践
    RetinaNet-Obj
    激活函数总结(四十四):激活函数补充(NLSIG、EvoNorms)
    Double精度丢失问题排查及解决思路
    SpringBoot自定义classloader加密保护class文件
  • 原文地址:https://blog.csdn.net/weixin_45715405/article/details/126716323