对多线程经验不多,仅提供一些个人看法,如有错误请指正。 1.C++0X 多线程简介C++0x STL提供了对多线程的支持就不用再去选择跨平台的多线程库了,用标准的吧:) 看了一下BOOST和当前STL的接口几乎完全一致:)也就是说用boost thread写的程序应该把例如boost::thread, boost::unique_lock ...等等的地方换成std::thread, std::unique_lock...就OK了,个人觉得,不过我还没用过boost thread.所以说熟悉pthread的应该能很快上手,而熟悉boost thread应就可以直接上手了~ 但是现在GCC还不支持thread local变量。不支持原子操作。基本的mutex,condional 变量等等都支持了。 关于C++0X thread的入门介绍请参考:
该文有中文的翻译
另外使用C++0X的话大家最好了解一下所谓的右值引用和move语义,个人觉得这两个概念很有用处的,算是突破性的概念了,当然不清楚也可以用thread.参考刘未鹏写的,《C++0x漫谈》系列之:右值引用(或“move语意与完美转发”)(上),既可http://blog.csdn.net/pongba/archive/2007/07/10/1684519.aspx 或者看A Brief Introduction to Rvalue References http://www./jtc1/sc22/wg21/docs/papers/2006/n2027.html 2. 如何编译运行我只在linux下用GCC4.4.2试过。能够调试成功。 首先你要确保你的GCC编译时是thread enable的一般默认安装就是enable的了。可以用gcc -v查看一下: allen:~/study/unix_system/CH14$ gcc -v 如上显示线程模型posix,表示就是可以用多线程了否则显示为空。注意本质上还是posix多线程所以你要加编译参数-pthread 同时标明使用 c++0x特性, -std=c++0x 或者-std=gnu0x 例如: g++ -g -o wordcount wordcount.cc -std=c++0x -pthread 3.一个完整的小程序示例,多线程文本单词数目统计
代码
/** * ============================================================================== * * \file wordcount.cc * * \author chenghuige@gmail.com * * \date 2009-12-02 09:55:25.197674 * * Description: 演示c++0x,线程建立结束,mutex,条件变量的使用,注意为了简单名字未加 * std:: * ============================================================================== */ #include <iostream> #include <fstream> #include <string> #include <thread> using namespace std; mutex m; condition_variable reader_cond; //cond notify reader condition_variable writer_cond; //cond2 notify writer struct MailBox { string file_name; //which file int count; //how many words int tid; //which thread write }; MailBox mail_box; class WordCounter { private: int tid_; string infile_name_; public: WordCounter(int tid, string infile_name): tid_(tid),infile_name_(infile_name){} void operator()() { int c, prevc = '\0'; int count = 0; //统计文本的单词数目 ifstream input_file(infile_name_.c_str(),ios::binary); input_file.unsetf(ios::skipws); // 要接受空格符 istreambuf_iterator<char> eos; // end-of-range iterator istreambuf_iterator<char> iit (input_file); for (; iit!=eos; ++iit) { c = *iit; if ( !isalnum(c) && isalnum(prevc) ) count++; prevc = c; } input_file.close(); cout << "COUNT " << tid_ << " waiting to get lock" << endl; unique_lock<mutex> lk(m); cout << "COUNT " << tid_ << " have lock, store data" << endl; //如果mail_box不是空的,注意mail_box是互斥区所保护的 while (!mail_box.file_name.empty()) { cout << "COUNT " << tid_ << " oops.. mail box not empty, wait for signal" << endl; //等待读者读完并通知,写着释放锁控制 writer_cond.wait(lk); //注意wait只接受unique_lock不接受lock_guard //重新锁住互斥区 } cout << "COUNT " << tid_ << " OK, I can write mail" << endl; mail_box.file_name = infile_name_; mail_box.count = count; mail_box.tid = tid_; cout << "COUNT " << tid_ << " rasing flag" << endl; //通知读者我已经写完你可以读了 reader_cond.notify_one(); //注意在这里我还是拥有锁的,所以尽管读者已经借到可读通知了,但是它还是会被卡到互斥区外的 cout << "COUNT " << tid_ << " Finished writting.Words are " << count << " for file " << infile_name_ << endl; cout << "COUNT " << tid_ << " will unlock" << endl; //在结束的时候lk的析构函数会自动调用解锁,注意这之后不一定是读者能够获得互斥区,有可能 //被其它写者抢先抢到但是没关系,它们发现mail_box非空,会wait,并且释放互斥区的,所以可能 //出现下面的情况 //COUNT 0 will unlock //COUNT 1 have lock, store data //COUNT 1 oops.. mail box not empty, wait for signal //COUNT 2 have lock, store data //COUNT 2 oops.. mail box not empty, wait for signal //MAIN: Wow! flag was raised, I have the lock //11 1.log 0 //Main has finished reading } }; void read_mail(char *argv[]) { //读者要先锁住互斥区 unique_lock<mutex> lk(m); cout << "Main locking the box" << endl; //建立3个线程,注意这里采用的是建立空线程, //然后利用临时变量的move,线程是不可拷贝但是可move的 thread t[3]; for (int i = 0; i < 3; i++) t[i] = thread(WordCounter(i, argv[i + 1])); int total_words = 0; //读者要等待3个写者统计完文本信息 for(int reports_in = 0; reports_in < 3; reports_in++) { cout << "MAIN: waiting for flag to go up" << endl; //等待一个写者写完mail,同时释放指定的锁m reader_cond.wait(lk); //收到写者写完的信号,到达这里,读者可以读了,同时锁住互斥区 cout << "MAIN: Wow! flag was raised, I have the lock" << endl; cout << mail_box.count << " " << mail_box.file_name << " " << mail_box.tid << endl; total_words += mail_box.count; //读者读完后将mail_box的文件名置空,标明已经读完 mail_box.file_name.clear(); cout << "Main has finished reading" << endl; //通知写者我已经读完mail box writer_cond.notify_one(); } for (int i =0; i < 3; i++) { t[i].join(); } cout << total_words << ": total words" << endl; } int main(int argc, char *argv[]) { if ( argc != 4 ){ cout << "usage: " << argv[0] << " file1 file2 file3" << endl; exit(1); } read_mail(argv); }
Main locking the box
上面的例子中加锁采用, unique_lock<mutex> lk(m); 这样在函数结束时析构函数自动解锁,那么如果我想控制加锁解锁的位置呢,可以用下面的方法.
std::unique_lock<std::mutex> lk(m, std::defer_lock); //不加锁 lk.lock() ... lk.unlock()
首先上面我用了两个条件变量,因为不这样,用一个cond那么,如果一个写者写完后notify reader,之后释放锁,但之后抢锁的过程还是写者抢到,发现mail是空的 在cond上block并且释放锁,然后又是另一写者抢到锁,同样block再释放锁,但是这时候在cond上就有两个写者了,所以后续发生了cond上有一个读者一个写者, 然后可能发生写者notify却唤醒另一个写者的情况,产生死锁。 解决方法:1,就是我上面的程序用两个cond 2. 对于写者自己前面再加一个锁,保证只能会有一个写者停在cond上 3. 这个当时没想到,最好的方法其实是读者,读完notify_all唤醒所有可能卡住的写者。这样也可以保证cond上不会同时又读者和写者。 关于为什么用条件变量而不用信号量,网上中文的解释很少,查到一篇老外的文章: http://www./projects/dist/cond-var/Condition_Variable_FAQ 另外更好的解释是一个课件,这个对锁,信号量,条件变量解释的太好了: http://www.eecs./~mdw/course/cs61/mediawiki/images/7/7e/Lectures-semaphores.pdf
|
|