关于C++的跨平台多线程库,这里列举了一些知名的库:
其中,tinythread++是个很轻量级的,我们这里只介绍它
TinyThread++只有一个cpp文件和两个h文件,使用时,将它们放在自己的工程中一道编译。
TinyThread++的主页,可以下载这个库。下载解压,进入TinyThread++目录,主要有source和test两个子目录,前者包含TinyThread++的三个文件,后者包含三个简单的示例程序,其中test.cpp比较详细地列举了9种应用。
下面是个使用detach的thread的小例子:
#include <iostream> #include <sstream> #include <tinythread.h> using namespace std; using namespace tthread; struct inputData { int len; const char* data; }; void ThreadProc(void *aArg) { inputData input = *((inputData*)aArg); string s; s.assign(input.data, input.len); cout << "Sub thread id: " << this_thread::get_id() << " input data: " << s << endl; } int main(int argc, char* argv[]) { cout << "this thread id: " << this_thread::get_id() << endl; for(int i = 0; i < 3; ++i) { inputData input; string s; switch(i) { case 0: s = "#Data0#"; break; case 1: s = "#Data1#"; break; case 2: s = "#Data2#"; break; } input.len = s.size(); input.data = s.c_str(); thread t(ThreadProc, &input); t.detach(); //sleep(1); } cout << "..................." << endl; while(1) sleep(1); return 0; }其目的是,在主线程中创建3个从线程,分别传给它们不同的inputData输入参数,主要是输入的字符串内容不一样。希望在每个从线程中输出传入的字符串,然后自行退出。
实际输出的一种结果是:
this thread id: 1 Sub thread id: 2 input data: #Data1# ................... Sub thread id: 3 input data: #Data2# Sub thread id: 4 input data: #Data2#按照我们期望的,2号线程,输出的input data部分应该是 #Data1#,3号,应该是#Data1#,实际得到的结果和设想的不一致。
调试发现,每次走到第16行的时候,指针aArg的值是一样的!即,22行申明的、44行传给ThreadProc函数的临时变量input,每次都在同样的内存空间中分配。于是,3个从线程,都从同一块内存区域读取输入参数值。这就造成,有可能前一个线程还没来得及从那段内存中读取数据,那段内存就在创建下一个从线程的时候被重新赋值(覆盖)了。
对应上面的实际输出,覆盖操作就是:
2号线程还没来得及读,就被为3号线程而做的赋值操作给覆盖了,造成2号线程本该输出#Data0#的,却输出了#Data1#;
3号线程还没来得及读,就被为4号线程而做的赋值操作给覆盖了,造成3号线程本该输出#Data1#的,却输出了#Data2#。
事实上,如果我们放开46行的sleep操作,就不会出现上面的问题了,因为两个相邻的“创建从线程”的操作之间sleep了1秒钟,这段时间足以让前一个从线程在临时变量input未被覆盖的情况下从中读取它想要的数据!
输入给从线程的数据不使用栈上的临时变量,而是在堆上分配空间,每次都new出新的input变量(对应不同的内存空间),各个从线程从各自的堆空间中读取自己想要的数据,相互不干扰。从线程使用完毕堆上的数据后,自己将这些数据delete掉,然后自行退出。代码如下:
#include <iostream> #include <sstream> #include <tinythread.h> #include <string.h> using namespace std; using namespace tthread; struct inputData { int len; char* data; }; void ThreadProc(void *aArg) { inputData input = *((inputData*)aArg); string s; s.assign(input.data, input.len); cout << "Sub thread id: " << this_thread::get_id() << " input data: " << s << endl; delete [] input.data; delete aArg; } int main(int argc, char* argv[]) { cout << "this thread id: " << this_thread::get_id() << endl; for(int i = 0; i < 3; ++i) { inputData *input = new inputData; input->data = new char[20]; input->len = strlen("#Data0"); switch(i) { case 0: strcpy(input->data, "#Data0#"); break; case 1: strcpy(input->data, "#Data1#"); break; case 2: strcpy(input->data, "#Data2#"); break; } thread t(ThreadProc, (void*)input); t.detach(); } cout << "..................." << endl; while(1) sleep(1); return 0; }
本示例改正后的工程在这里
thread::~thread() { if(joinable()) std::terminate(); }如果是joinable的,就立即销毁该线程相关的资源;否则,什么也不做。