C++11 下的线程编程
Table of Contents
C++11 中正式引入了标准类 std::thread
, 也就是说,你编写使用线程的程序时不再需要使用 C 下面的 pthread 等 API, 可以直接用 C++11 标准的库来实现线程了。
本笔记的主要运行平台为 Debian Linux 7.1 (wheezy), gcc 4.7.2. 使用 GCC 编译笔记中的代码,需要注意:
- 如果 GCC 版本低于 4.7, 需要添加编译参数
-std=gnu++0x -pthread
- 如果 GCC 版本在 4.7 以上,需要添加编译参数
-std=c++11 -pthread
GCC 各版本对 C++11 特性的支持情况参见: http://gcc.gnu.org/projects/cxx0x.html
用线程运行一个简单的函数
直接使用头文件 <thread>
中包含的标准类 std::thread
, 示例如下:
#include <thread> #include <iostream> void my_thread_func() { std::cout<<"hello"<<std::endl; } int main() { std::thread t(my_thread_func); }
编译:
g++ -std=c++11 -pthread a.cpp
但执行后无任何输出。如果想获得线程的执行结果,需要将其连接 (joining) 起来,因此,上述代码需要在 std::thread t(my_thread_func);
声明之后添加一句:
t.join();
要想让线程在指定地方结束,必须等待它完成。
异步执行 (async) 一个线程
假如想让一个函数每隔 1 秒钟将全局计数器增加一个计数,又不想让这个函数影响到主线程的执行。那么可以用 std::async
来实现。例如:
#include <future> #include <chrono> #include <iostream> static int fCount; void called_from_async() { fCount++; std::cout << "Async call: " << fCount << ", " << asString(tp) << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); called_from_async(); } int main() { fCount = 0; // called_from_async launched in a separate thread if possible std::future<void> result(std::async(called_from_async)); for(int i=0; i<20; i++) { std::cout << "Message from main: " << i << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } // Ensure that called_from_async is launched synchronously // if it wasn't already launched result.get(); return 0; }
但上述程序中 called_from_async
并不会马上执行,而是等待中间的循环结束后,才开始。这是为什么呢?因为 std:async
的启动策略 (launch policy) 默认设置为
std::launch::any
, 意思是说,只有在你明确表示要等待其结果时才开始执行。如果要让函数立刻执行,则需要修改启动策略为 std::launch::async
. 同样,如果你确实希望函数只须在 get()
被调用时才执行,则明确指定其启动策略为 std::launch::sync
.
完整的程序如下:
#include <future> #include <thread> #include <chrono> #include <iostream> static bool fQuit; static int fCount; std::string asString (const std::chrono::system_clock::time_point& tp) { // convert to system time: std::time_t t = std::chrono::system_clock::to_time_t(tp); std::string ts = std::ctime(&t);// convert to calendar time ts.resize(ts.size()-1); // skip trailing newline return ts; } void called_from_async() { fCount++; auto tp = std::chrono::system_clock::now(); std::cout << "Async call: " << fCount << ", " << asString(tp) << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); if(not fQuit) called_from_async(); } int main() { fCount = 0; fQuit = false; // called_from_async launched in a separate thread if possible std::future<void> result(std::async(std::launch::async,called_from_async)); for(int i=0; i<20; i++) { std::cout << "Message from main: " << i << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } // Ensure that called_from_async is launched synchronously // if it wasn't already launched fQuit = true; result.get(); return 0; }
上述程序会在主程序输出消息的中间穿插着异步线程的输出结果,比如:
$ ./a.out Message from main: 0 Async call: 1, Wed Aug 21 15:27:26 2013 Message from main: 1 Async call: 2, Wed Aug 21 15:27:27 2013 Message from main: 2 Async call: 3, Wed Aug 21 15:27:28 2013 Message from main: 3 Async call: 4, Wed Aug 21 15:27:29 2013 Message from main: 4 ...
而这正是我想要的结果。