当前位置:   article > 正文

c++11多线程(thread)_c++11 thread move

c++11 thread move

std::thread介绍

std::thread是c++11的特性,提供管理多线程的类。

note: 注意下文中线程和线程对象的区别:线程即通俗意义上的线程,而**线程对象(thread对象)**这里指的是thread类的一个具体实例

thread的常用成员函数

  1. detach(): 将线程和thread对象分离,分离后线程和线程对象没有任何联系
  2. join(): 连接线程,并等待线程执行完毕才返回。
  3. joinable(): 判断线程线程对象是否可连接,如果thread对象管理着某个线程(不管这个线程有没有在执行),则为可连接状态,反之则不是。
  4. thread& operator=(thread&& _Other):右值引用赋值操作,实现将临时thread对象赋值给thread对象。
  5. swap:和另一个thread对象交换管理的线程
  6. native_handle:获得线程的句柄

详细说明

  1. thread对象和线程是一对一的关系(通过删除拷贝构造函数和左值赋值函数实现,见下面的源码),即一个thread对象只能管理一个线程,当然也可以不管理任何线程,比如使用默认构造函数,move构造,join或者detach之后,线程对象就不再管理线程。
  2. thread的“=”操作,实际上是通过右值引用实现了对临时线程对象的控制权转移,即将线程的管理权从一个临时的线程对象移动到另一线程对象。注:线程对象删除了左值引用赋值的操作。这意味着只能将一个临时对象赋值给线程对象,而不能将一个线程对象赋值给另一个线程变量。这也符合其对象和线程一一对应的设定。
    其删除了用左值进行赋值的操作,源码如下:
thread& operator=(thread&& _Other) _NOEXCEPT
{	// move from _Other
	return (_Move_thread(_Other));
}
thread(const thread&) = delete;  //删除拷贝构造函数
thread& operator=(const thread&) = delete; //删除了左值引用的赋值操作
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

所以有如下程序段的问题:

std::thread t1;
std::thread t2;
t1 = t2; //错误:左值的赋值操作已被删除
t1 = std::thread(fun); //正确
  • 1
  • 2
  • 3
  • 4

所以thread对象获得线程管理权有两种方式:1)通过构造函数;2)通过右值赋值操作。

  1. deach()操作使线程独立于thread对象执行,调用后线程和该thread对象没有任何关系。通过get_id()获得线程id = 0(即没有线程)。
  2. joinable()join()可以用于在销毁销毁thread对象前,等待线程执行结束,避免主线程退出时,其他线程还在继续执行,并访问主线程的相关数据,而导致程序崩溃。
  3. 线程对象绑定具体的线程后一定要在thread对象销毁之前,执行detach()或者join(),否则程序会崩溃。崩溃的原因:thread对象析构时会调用如下函数,如果线程对象之前没有调用detach()或者join()方法,析构时会执行terminate(),导致程序崩溃。
thread& _Move_thread(thread& _Other)
{	// move from _Other
	if (joinable())
		_XSTD terminate();  //崩溃原因:terminate()用于终止程序运行。
	_Thr = _Other._Thr;
	_Thr_set_null(_Other._Thr);
	return (*this);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这也符合thread作为线程管理对象的设定,析构时如果线程还在运行就不能被析构,否则其管理职责就失去了意义。

thread具体使用方法

创建线程

#include <iostream>
#include <string>
#include <thread>
#include <windows.h>
using namespace std;

void fun1(){
	while (true) {
		cout << "run in sub thread!"<<endl;
		Sleep(100);
	}
}

void fun2(const std::string &str) {
	while (true) {
		cout << "run in sub thread with para:" << str<<endl;
		Sleep(100);
	}
}
void main()
{
	//通过构造函数创建线程
	std::thread t1(fun1);
	//创建线程,并传入参数
	std::thread t2(fun2, "subthread2");
	//用lambda表达式创建线程
	std::thread t3([]() {
		while (true) {
			cout << "run in lambda exp thread" << endl;
			Sleep(100);
		}
	});
	t1.detach(); t2.detach(); t3.detach();
	system("pause");
}
  • 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

类中使用thread
经常我们需要在执行某个类的方法时开启一个子线程,如果子线程中使用了对象的成员变量,则需要保证在类对象析构前执行完子线程的内容,否则会报内存访问的错误。可以通过如下方式解决这个问题:
下面的例子中,在类的mfun方法中开启了子线程,线程对象并没马上调用detach()或者join()操作,而是在类析构函数中调用,这样就可以保证等子线程执行结束后再析构类的对象。

class ThreadUser {
public:
	~ThreadUser(){
		if (subThread.joinable())   //等待线程执行完毕在释放thread对象
			subThread.join();
	}
	void mfun() {
		subThread = thread([&]() {
			for(int i = 0; i < num; i++)
				cout <<"i"<<endl; 
		});
	}
private:
	thread subThread;
	int num;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

类的成员函数作为线程执行函数

类成员函数可以作为thread的线程函数进行执行,但需要传入类的指针作为参数,方法如下:

class ThreadUser {
public:
	void tfun() {
		cout << "threadsuser fun" << endl;
	}
};

void main() {
	thread tr(&ThreadUser::tfun, new ThreadUser());  //用ThreadUser对象的成员函数tfun作为线程函数,&<类名>::<成员函数名> 为获得类的成员函数指针的写法。
	system("pause");
}	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/153591
推荐阅读
相关标签
  

闽ICP备14008679号