管理任务
在C++ 11的thread库引入了std::async函数,通过它可以非常容易的实现类似于.net中的Task方式的并发。
void printf_thread_id(const
char* name)
{
cout << name << " Thread id: " << this_thread::get_id() << endl;
}
int main()
{
printf_thread_id("Main Thread");
std::future<const
char*> task1 = std::async(std::launch::async, []()
{
printf_thread_id("task 1");
return
"hello world";
});
task1.wait();
cout << task1.get() << endl;
}
可以看到:可以通过std::async函数创建并执行一个任务,返回值为一个future对象,它提供了wait函数等待任务的执行,get函数获取任务的返回值(这个函数内部会调用wait,任务未执行完成的时候会等待),和.Net中的Task非常类似。
下面是个稍微复杂点的例子:
#include
<iostream>
#include
<future>
#include
<thread>
using
namespace std;
void printf_thread_id(const
char* name)
{
cout << name << " Thread id: " << this_thread::get_id() << endl;
}
int main()
{
printf_thread_id("Main Thread");
auto begin = chrono::system_clock::now();
auto task1 = std::async([]()
{
printf_thread_id("task 1");
this_thread::sleep_for(chrono::seconds(1));
return 3;
});
auto task2 = std::async([]()
{
printf_thread_id("task 2");
this_thread::sleep_for(chrono::seconds(1));
return 5;
});
task1.wait();
task2.wait();
auto end = chrono::system_clock::now();
cout << "total value " << task1.get() + task2.get() << endl;
cout << "spent time " << chrono::duration_cast<chrono::milliseconds>(end - begin).count() << endl;
}
设置任务的并行方式
可以通过stl::async函数的第一个参数控制任务的并行方式,它有如下三个取值:
- launch::async 异步方式。这个方式下,所有任务都会新启动一个线程执行
- launch::deferred 同步方式。 这个方式下,任务不会新启动线程,串行在创建任务的线程中执行。
- launch::any 综合方式。 这个方式下,会复用创建任务的线程。
这个参数也可以不带(我例2中的方式就没有带),默认情况下是launch::any。
手动控制并发
前面介绍过可以通过在async函数中通过参数设置并发方式,不过这些基本上都是些自动化的手段,有的时候粗了点,需要我们手动控制。例如:100个任务,但同时最大执行5个并发。
要手动控制任务,就不能通过async函数启动任务了,必须我们自己手动创建对象,并将其放入线程中执行。示例代码如下:
#include
<iostream>
#include
<future>
#include
<thread>
int main()
{
std::packaged_task<int()> task([](){return 7;}); // wrap the function
std::future<int> result = task.get_future(); // get a future
std::thread(std::move(task)).detach(); // launch on a thread
std::cout << "Waiting...";
result.wait();
std::cout << "Done!\nResult is " << result.get() << '\n';
}
捕获异常
和.net中的处理方式一样,任务处理过程中产生的异常可以在任务外通过get函数捕捉,非常方便。
int main()
{
auto ftr = std::async([]()
{
throw std::exception("Error occurs !");
});
try
{
ftr.get();
}
catch(std::exception & e)
{
std::cout << e.what() << std::endl;
}
}
需要注意的是,wait函数并不像.net中的那样会抛异常,通过wait函数是捕获不到异常的。