同步并发操作之等待一次性事件

有时候需要用一些后台线程来完成计算,这些计算往往都是一次性的,线程计算完后便结束。这时候可以使用条件变量,但是有点浪费,我们只需要获取一次结果。C++标准库中有头文件<future>,很形象“未来”,获取未来计算的结果。

使用std::async来启动一个异步任务。用std::future对象来存储异步任务返回的结果,这个对象存储结果。当我们需要结果时,只需调用get()方法。这时如果还没计算完毕,当前线程会阻塞。

#include<future>
#include<iostream>

int find_the_answer_to_ltuae();
void do_other_sutff();
int main()
{
	std::future<int> the_answer = std::async(find_the_answer_to_ltuae);
	do_other_sutff();
	std::cout << "The answer is " << the_answer.get() << std::endl;
	return 0;
}
int find_the_answer_to_ltuae()
{

	return 10;
}
void do_other_sutff()
{
	std::cout << "do_other_sutff " << std::endl;
}

可以像线程那样,向函数传递参数

#include<future>
#include<iostream>
#include<string>

struct X
{
	void foo(int, std::string const&);
	std::string bar(std::string const&);
};
X x;
auto f1 = std::async(&X::foo, &x, 32, "hello");//p->foo(42,"hello")。p是&x
auto f2 = std::async(&X::bar, x, "goodbye");//tmp.bar("goodbye")。x是tmp

struct Y
{
	double operator()(double);
};
Y y;
auto f3 = std::async(Y(), 3.141);//tem(3.141),tmp是Y()的move-constructed
auto f4 = std::async(std::ref(y), 2.718);//y(2.718)
X baz(X&);
std::async(baz, std::ref(x));//调用baz
class move_only
{
public:
	move_only();
	move_only(move_only&&);
	move_only(move_only const&) = delete;
	move_only& operator=(move_only&&);
	move_only& operator=(move_only const&)=delete;
	void operator()();
};
auto f5 = std::async(move_only());//构造临时对象执行

可以通过传递参数来决定是否启动新的线程或何时启动新线程,

auto f6 = std::async(std::launch::async, Y(), 1.2);//启动新线程执行
auto f7 = std::async(std::launch::deferred, baz, std::ref(x));//调用wait或get后才执行
auto f8 = std::async(
	std::launch::deferred | std::launch::async,
	baz, std::ref(x));//由实现来选择
auto f9 = std::async(baz, std::ref(x));
f7.wait();//执行f7对应的后台线程

结合task和future

可以使用std::packaged_task<>和future结合。当激活std::package_task<>时,调用future的函数。函数返回结果存在关联的数据中。

std::package_task<>的参数是函数签名(像函数指针定义)。例如,void()表示无返回值,无参数的函数;int (std::sgring&,double*)表示函数返回类型为int,参数为string引用和double类型指针。当定义std::package_task<>对象时,必须给出参数和返回类型。

std::future<>的返回类型通过成员函数get_future()获得。

在许多GUI框架中,要求从特定的线程更新GUI。如果一个线程想要更新GUI,它必须给GUI线程发送一个消息。可以通过std::package_task来解决这个问题。

#include<deque>
#include<mutex>
#include<future>
#include<thread>
#include<utility>

std::mutex m;
std::deque<std::packaged_task<void()> > tasks;

bool gui_shutdown_message_received();
void get_and_process_gui_message();

void gui_thread()//GUI线程
{
	while(!gui_shutdown_message_received())//收到GUI关闭信息
	{
		get_and_process_gui_message();//处理GUI窗口信息
		std::packaged_task<void()> task;//定义任务
		{
			std::lock_guard<std::mutex> lk(m);
			if(tesks.empty())//任务队列无任务时,执行task()
				continue;
			task=std::move(tasks.front());
			tasks.pop_front();//任务出列
		}
		task();//执行task
	}
}

std::thread gui_bg_thread(gui_thread);
template<typename Func>
std::future<void> post_task_for_gui_thread(Func f)
{
	std::package_task<void()> task(f);//创建task
	std::future<void> res=task.get_future();
	std::lock_guard<std::mutex> lk(m);
	tasks.push_back(std::move(task));//把task放到队列
	return res;
}
时间: 2024-10-09 20:23:05

同步并发操作之等待一次性事件的相关文章

同步并发操作之等待一个事件或条件

两个线程需要同步操作时,可以设置一个全局变量,用互斥量保护这个全局变量,通过这个全局变量来同步. 但是这样太浪费CPU,这时可以用休眠方法. bool flag; std::mutex m; void wait_for_flag() { std::unique_lock<std::mutex> lk(m); while(!flag) { lk.unlock(); //休眠期间,其他线程可以操作flag std::this_thread::sleep_for(std::chrono::milli

《C++ Concurrency in Action》读书笔记三 同步并发操作

本章要点 *等待事件 *使用futures等待一次性事件(waiting for one-off events with futures) *等待时间限制 *使用同步操作来简化代码 这章主要描述了如何使用条件变量和futures来等待事件,以及如何使用他们来使线程同步操作更加简化. CP4 1. 等待事件或者其他条件 a.如果一个线程需要等待另外一个线程处理的结果,可以采取不停的检测共享资源,等另外一个线程完成特定操作以后会修改共享资源.该线程检测到修改以后执行后续的操作,但这种方法需要不停的检

Cpp Concurrency In Action(读书笔记3)——同步并发操作

等待一个事件或其他条件 第一,它可以持续的检查共享数据标志(用于做保护工作的互斥量),直到另一线程完成工作时对这个标志进行重设. 第二个选择是在等待线程在检查间隙,使用 std::this_thread::sleep_for() 进行周期性的间歇: #include <iostream> #include <thread> #include <mutex> class wait_test { bool flag; std::mutex m; public: wait_t

c/c++ 多线程 等待一次性事件 packaged_task用法

多线程 等待一次性事件 packaged_task用法 背景:不是很明白,不知道为了解决什么业务场景,感觉std::asynck可以优雅的搞定一切,一次等待性事件,为什么还有个packaged_task. 用法:和std::async一样,也能够返回std::future,通过调用get_future方法.也可以通过future得到线程的返回值. 特点: 1,是个模板类,模板类型是个方法类型,比如double(int),有一个参数,类型是int,返回值类型是double. std::packag

c/c++ 多线程 等待一次性事件 std::promise用法

多线程 等待一次性事件 std::promise用法 背景:不是很明白,不知道为了解决什么业务场景,感觉std::async可以优雅的搞定一切的一次等待性事件,为什么还有个std::promise. 用法:和std::async一样,也能够返回std::future,通过调用get_future方法.也可以通过future得到线程的返回值. 特点: 1,是个模板类,模板类型是个方法类型,比如double(int),有一个参数,类型是int,返回值类型是double. std::promise<i

c/c++ 多线程 等待一次性事件 异常处理

多线程 等待一次性事件 异常处理 背景:假设某个future在等待另一个线程结束,但是在被future等待的线程里发生了异常(throw一个异常A),这时怎么处理. 结果:假设发生了上面的场景,则在调用future的get方法时,就会得到被future等待的线程抛出的异常A. 3种情况: 1,std::async 2,std::packaged_task 3,std::promise,知道发生异常了,可以不调用set_value,而是调用set_exception(std::current_ex

[C++11 并发编程] 13 使用期望等待一次性事件

C++标准库使用期望(future)来支持一次性事件的等待.要等待某种一次性事件的线程可以获取一个代表该该事件的期望.这个线程可以每隔一段事件周期性的查询这个期望.此外,这个线程也可以继续做其它的处理,直到需要等待这个一次性事件才被挂起.通过期望还可以可以传递数据. C++标准库提供了两种期望unique future(std::future<>)和shared futures(std::shared_future<>),都声明在<future>库头文件中.std::f

c/c++ 多线程 多个线程等待同一个线程的一次性事件

多线程 多个线程等待一个线程的一次性事件 背景:从多个线程访问同一个std::future,也就是多个线程都在等待同一个线程的结果,这时怎么处理. 办法:由于std::future只能被调用一次get方法,也就是只能被某一个线程等待(同步)一次,不支持被多个线程等待.所以std::sharted_future,就应运而生了. 特点:std::sharted_future可以被复制,std::future是不可以的被复制的. std::sharted_future的3种创建方式(fut为std::

Python3标准库:threading进程中管理并发操作

1. threading进程中管理并发操作 threading模块提供了管理多个线程执行的API,允许程序在同一个进程空间并发的运行多个操作. 1.1 Thread对象 要使用Thread,最简单的方法就是用一个目标函数实例化一个Thread对象,并调用start()让它开始工作. import threading def worker(): """thread worker function""" print('Worker') threads