C++中多线程的创建方式总结

使用函数作为参数创建线程

函数无参

使用一个无参函数创建线程,thread 的构造接受一个参数,即函数名。

//编写一个函数作为线程的执行路径
void myprint() {
	cout << "我的线程开始运行" << endl;
	cout << "我的线程开始运行" << endl;
	cout << "我的线程开始运行" << endl;
	cout << "我的线程开始运行" << endl;
	cout << "我的线程开始运行" << endl;
	cout << "我的线程开始运行" << endl;  
}

//在main中:
//使用函数作为参数创建一个线程对象
	thread mythread(myprint);
	//join方法将使主线程阻塞,等待子线程执行完毕,子线程执行完毕后再“汇合”,主线程继续执行。
	mythread.join();
	
	//detach方法不会使主线程阻塞,它会与主线程一起运行, 当主线程执行完毕而调用detach的线程还未执行完毕h时:
		//它会被c++运行时库接管
	//mythread.detach();
函数有参

使用一个有参函数创建线程,thread 的构造接受第一个参数为函数名,之后接受若干参数,分别为函数的各个参数。

//此处引用作为参数应该使用const修饰,否则将报错
void print(const int& i, char* p) {
	cout << i << endl;
	cout << p << endl;
}

	//在main中:
	int i = 10;
	char chs[] = "hello world!";
	thread mythread10(print, i, chs);
	mythread10.join();
  

可以通过将类作为函数参数, 并重写类中构造,析构,拷贝构造等方法的方式测试到:通过函数参数传递数据的过程是传递的数据的拷贝(即使函数形参数使用的是引用)。

//类Ta代码在本文后面
void fun(const Ta& a) {
	cout << "a.i=" << a.i << endl;
}
int main() {
	Ta t;
	thread mythread2(fun, t);
	mythread2.join();
	return 0; 
}

在多线程中,无论采用值传递还是引用传递,编译器为了数据安全,都会创建一个新对象传递给其他线程(这里指mythread),为了使传递给其他线程的不是新对象,而是原来对象的引用(即希望线程中对该数据的修改能够影响主线程中的值),应该使用其他方式。

使子线程结果影响主线程unique_ptr和ref()

使用独占指针作为函数参数unique_ptr,可以使得传递到子线程的不是副本。

void unique_ptr_fun(unique_ptr<Ta> uniqueTa) {
	cout << "OK" << endl;
}
	//main中
	unique_ptr<Ta> uniqueTa(new Ta(10));
	//这种调用将报错,应该使用将对象所有权转移的move()函数 
	//thread mythread2(unique_ptr_fun, uniqueTa);
	thread mythread2(unique_ptr_fun, move(uniqueTa));
	mythread2.join();

由于使用智能指针,子线程中的指针指向一块主线程创建的内存,所有要使用join而不是detach,否则会可能让子线程中指针指向一块被释放了的内存的危险。
而且这样将导致main中的指向该块数据的智能指针不能使用了。所有,应该用ref方法来完成。

这种用std::ref的调用方式不会调用拷贝构造函数,而是直接将原来对象传递过去,其他线程对数据的修改会影响主线程,即这种方式失去了对原始数据的保护.

void fun2(Ta& ta) {
	ta.i = 100;
}
	//main中:
	Ta ta(10);
	cout << "调用mythread2之前 m=" << ta.i << endl; 
	//通过输出的对象地址相同可以发现这一点
	thread mythread2(fun2, std::ref(ta));
	mythread2.join();
	cout << "调用mythread2之后 m=" <<ta.i<< endl; 

数据失去保护后,如果用detach(),将导致严重问题,一定要用join().

使用类作为线程对象的参数

通过重载运算符()后,传入对象,编译器便从()的重载函数作为线程的执行入口。这里的创建线程调用了拷贝构造函数

class Ta {
	int i;
public: 
	Ta(int _i) :i(_i) { cout << "构造函数执行" << endl; }
	Ta(const Ta& t) :i(t.i) { cout << "拷贝构造函数执行" << endl; }
	~Ta() { cout << "析构函数执行" << endl; }
public:
	void operator()() //不能带参数
	{
		cout << "对象中的线程开始执行" << endl;
		cout << "对象中的线程开始执行" << endl;
		cout << "对象中的线程开始执行" << endl;
	}
};

	//在main中 
	Ta ta;
	thread mythread(ta);
	mythread.join();
	//由于创建thread对象的时候调用拷贝构造函数将ObjectA深拷贝到Ta的函数内,所以当主线程退出后,
	//mythread2仍然能继续执行,但是由于主函数结束,所以控制台输出可能不完整
	//mythread2.detach();//也可以用detach()
使用类的成员函数作为线程入口

在类中添加应该public函数

void thread_work(int i) {
		cout << i << endl;
	}

传入多个参数,第一个是函数地址,第二个是对象名,然后依次是函数参数

	//使用成员函数作为线程入口
	Ta obj(23);
	//传入多个参数,第一个是函数地址,第二个是对象,第三个是函数参数
	thread mythread4(&Ta::thread_work, obj, 100);
	mythread4.join();

这里如果第二个参数用引用,即写成&obj,则传递的是原对象,而不是其拷贝

用lambda表达式定义线程

	auto mylambdathread = [] {
		cout << "用lambda表达式创建的表达式开始执行" << endl;  
	};
	thread mythread3(mylambdathread);
	mythread3.join();

this_thread::get_id() 获取当前所在线程的id
joinable() joinable判断是否可以join或detach

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
1. 创建一个基于对话框的应用程序。并增加如图所示控件;分别为3个进度条控件关联三个进度条类型的变量;并在对话框的初始化函数,设定进度条的范围;为编辑框关联一个整型的变量;为12个按钮添加消息处理函数; 2. 定义结构体:用做线程函数的参数传递 typedef struct Threadinfo{ CProgressCtrl *progress;//进度条对象 int speed; //进度条速度 int pos; //进度条位置 } thread,*lpthread; 3. 为对话框增加三个句柄,用于标识各个线程; HANDLE hThread1; //线程1线程句柄 HANDLE hThread2; //线程2线程句柄 HANDLE hThread3; //线程3线程句柄 在增加三个结构体类型的变量,用做线程函数的参数传递; HANDLE hThread1; //线程1线程句柄 HANDLE hThread2; //线程2线程句柄 HANDLE hThread3; //线程3线程句柄 4. 新增一个静态的全局变量,用于记录所有线程的状态:static int GlobalVar=10000; 5. 声明并编写线程函数,注意只能有一个参数,且函数的返回值类型也是固定的;函数名可以自定义; DWORD WINAPI ThreadFun(LPVOID pthread);//线程入口函数 6. 在启动按钮的消息处理函数编写如下代码: thread1.progress=&m_progress1;//进度条对象 thread1.speed=100;//速度 thread1.pos=0;//初始位置 hThread1=CreateThread(NULL,0,ThreadFun,&thread1;,0,0);//创建并开始线程 if (!hThread1) { MessageBox("创建线程失败"); } 7. 编写线程函数(一般是一个死循环,或者需要花费时间很长的算法!否者就失去了多线程的意义) DWORD WINAPI ThreadFun(LPVOID pthread) //线程入口函数 { lpthread temp=(lpthread)pthread;//参数强制转换为结构体类型 temp->progress->SetPos(temp->pos); //设置被传递过来的进度条的位置 while(temp->posspeed); /设置速度 temp->pos++; //增加进度 temp->progress->SetPos(temp->pos); //设置进度条的新位置 GlobalVar--; if(temp->pos==20) { temp->pos=0; //进度条满则归0 } } return true; } 8. 在挂起按钮函数,编写如下代码: if(SuspendThread(hThread1)==0xFFFFFFFF) { MessageBox("挂起失败!进程可能已经死亡或未创建!"); return; } 9. 在执行按钮函数,编写如下代码: if(ResumeThread(hThread1)==0xFFFFFFFF) { MessageBox("执行失败!进程可能已经死亡或未创建!"); return; } 10. 在停止按钮函数,编写如下代码: if(TerminateThread(hThread1,0))//前些终止线程 { CloseHandle(hThread1);//销毁线程句柄 } else { MessageBox("终止进程失败!"); } 11. 为应用程序添加WM_TIMER消息,实时更新全局变量的值到编辑框;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值