Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
多线程是小型软件开发必然的趋势。C++11将多线程相关操作全部集成到标准库中了,省去了某些坑库的编译,真是大大的方便了软件开发。多线程这个库简单方便实用,下面给出简单的例子
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 | #include <iostream> #include <thread> #include <mutex> using namespace std; volatile int val; mutex mut; void icrement () { for (int i = 0; i < 100000000; i++) { mut.lock (); val++; mut.unlock (); } } int main (int argc, char* argv []) { //创建两个线程 thread t1 (icrement); thread t2 (icrement); //等待两个线程执行完 t1.join (); t2.join (); cout << val << endl; return 0; } |
概念有点多。首先得引入thread与mutex这两个头文件,其中thread与线程有关,mutex与锁有关。然后是两个全局变量,由于两个线程都会访问,所以加上volatile关键字,代表不要对这变量进行优化,然后是mutex,这个就是锁咯。
然后,下面的main函数中,首先创建两个线程,然后等待两个线程执行完,然后输出结果。最后是increment函数,这函数循环一亿次,不停的加锁解锁,控制着val变量的访问。锁还提供一个函数,比如这样调用 mut.try_lock (); ,立即返回bool类型,true代表加锁成功,false代表加锁失败。
这是最基本的情况。对于普通的加锁解锁操作,使用lock、unlock就够用了。但如果分支太多,这个就不好控制了。C++11提供了一种自动锁的机制,比如上面代码可以替换成这样:
1 2 3 4 5 6 7 | void icrement () { for (int i = 0; i < 100000000; i++) { //调用即加锁,离开大括号作用域自动解锁 lock_guard<mutex> lock (mut); val++; } } |
自动锁就不用刻意的解锁了,对于多分支的情况相当方便。
除了普通的锁之外,还有三种锁:
1 2 3 | recursive_mutex rm;//递归锁 recursive_timed_mutex rtm;//递归超时锁 timed_mutex tm;//超时锁 |
递归锁用在递归的场合,超时锁也就是普通锁加上超时功能,基本功能与锁相同。
锁方便控制代码逻辑,但如果只是控制访问一个变量的话,有一种更好的选择,那就是原子。详见C++11:原子操作
关于代码逻辑的控制,除了thread之外,还有一种,std::async,用于接收线程函数的返回值。关于返回值,通过thread的引用参数也可以传递,std::async只是让代码逻辑更清晰而已。示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <iostream> #include <string> #include <future> int main (int argc, char* argv []) { std::future<int> future = std::async (std::launch::async, [] () { std::this_thread::sleep_for (std::chrono::seconds (3)); return 4; }); std::future_status status; do { status = future.wait_for (std::chrono::seconds (1)); if (status == std::future_status::ready) { std::cout << "std::future_status::ready" << std::endl; } else if (status == std::future_status::timeout) { std::cout << "std::future_status::timeout" << std::endl; } else if (status == std::future_status::deferred) { std::cout << "std::future_status::deferred" << std::endl; } } while (status != std::future_status::ready); std::cout << future.get () << std::endl; return 0; } |
使用async首先需要引入future这个头文件。然后是std::async这个函数,它返回一个std::future这个模板类型,类型取决于函数的返回值。
第一个参数代表启动方式,std::launch::async代表调用函数时启动,std::launch::deferred代表创建后挂起线程,当执行std::future<>.get()时才执行线程。
第二个参数传入函数,可以是函数地址、lambda表达式或仿函数等。如果有参数的话,可以在调用std::async时加参数就行了。
这个线程执行两句话,sleep_for这行的意思是线程暂停3秒。个人感觉这语法太复杂了,还不如微软Sleep来的简洁。暂停线程之后,直接返回4。
然后 我们定义一个future的状态,future_status,用于获取线程执行结果。这儿可以直接用get(),表示等待线程执行完,那么就不用future了,也跟单线程没区别了,所以还是用wait_for来获取状态。获取状态后进行判断。如果为std::future_status::ready,代表线程已经执行完毕;如果为std::future_status::timeout,表示等待了wait_for传入的时间之后,线程还没执行完毕,还在继续执行;如果为std::future_status::deferred,代表线程还处于挂起状态。我用的VS2013 Communicaty Update4,发现无论如何也无法处于std::future_status::deferred状态,据说是我这个版本的bug,在VS2015中修复。个人建议,挂起线程这个尽量不用,调用get来获取还不如直接单线程来的直接。
然后是future.get,用于获取线程的返回值。如果线程并没执行完那么就一直等着。除此之外还有一个相似的函数,wait,用来等待线程执行结束,效果差不多。
以上代码不出所料,执行结果如下: