目录

异步编程和同步编程的区别

同步编程

异步编程

方法1:回调函数模拟传统异步

方法2:C++11中的std::async + std::future

std::async

std::future

std::shared_future

std::packaged_task

std::promise


异步编程和同步编程的区别

  • 同步编程:代码按顺序执行,会阻塞当前线程直到任务完成
  • 异步编程:任务发起后不等待结果,继续执行当前线程的后续代码,当前线程不被阻塞

下面根据几个例子来感受一下同步编程和异步编程的区别:

同步编程
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;

void syncTask()
{
	cout << "开始同步任务..." << endl;
	this_thread::sleep_for(chrono::seconds(2));//模拟2秒耗时
	cout << "同步任务完成" << endl;
}

int main()
{
	syncTask();//调用同步任务(会阻塞主线程)
	cout << "主进程继续执行" << endl;//必须syncTask执行完成后才会执行
	return 0;
}
异步编程
方法1:回调函数模拟传统异步
#include <iostream>
#include <thread>
#include <chrono>
#include <functional>
using namespace std;

using Callback = function<void()>;
void asyncTaskWithCallback(Callback callback)
{
	//创建新线程执行异步任务
	thread([callback]() {
		cout << "开始异步任务..." << endl;
		this_thread::sleep_for(chrono::seconds(2));//模拟2秒耗时
		cout << "异步任务完成" << endl;
		callback(); //异步任务完成后调用回调
		}).detach();//分离线程(不需要主线程等待)
}

int main()
{
	asyncTaskWithCallback([]() {
		cout << "回调函数被调用" << endl;
		});
	cout << "主进程继续执行" << endl;//主线程不会被阻塞
	this_thread::sleep_for(chrono::seconds(3)); //防止主线程提前退出
	return 0;
}
方法2:C++11中的std::async + std::future
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
using namespace std;

int Add(int num1, int num2)
{
	return num1 + num2;
}

int main()
{
	//启动异步任务并立即返回future对象
	future<int> result_future = async(std::launch::async, Add, 1, 5);
	cout << "主线程继续执行" << endl;//主线程不被阻塞
	this_thread::sleep_for(chrono::seconds(2));//模拟2秒耗时
	int ret = result_future.get(); //此时主线程会阻塞直到异步任务完成才获取结果
	cout << ret << endl;
	return 0;
}

std::async

  • std::async就是一个模板函数,内部会自动创建一个线程执行异步任务。

std::future

  • std::future是一个辅助我们获取异步结果的类,并不能单独使用,需要搭配一些能够执行异步任务的模板类或函数一起使用。注意:std::future::get()方法只能调用一次,调用后对应的future对象就会变为无效,再次调用就会抛出std::future_error异常。

std::shared_future

  • 可以多次调用get()方法,多个shared_future对象共享同一个异步任务结果,可以被拷贝。
int main()
{
	function<int(const int&, const int&)> func([](const int& x, const int& y) {
		this_thread::sleep_for(chrono::seconds(1));
		return x + y;});
	promise<int> result_promise;
	shared_future<int> shared_fut = result_promise.get_future().share();
	vector<thread> v;
	for (int i = 0;i < 3;++i) {
		v.emplace_back([shared_fut](){
			cout << this_thread::get_id() << "得到了异步任务结果:" << shared_fut.get() << endl;
		});
	}
	result_promise.set_value(func(1,5));
	for (int j = 0;j < v.size();++j) {
		if (v[j].joinable())
			v[j].join();
	}
	return 0;
}

std::packaged_task

  • 就是一个模板类,将函数指针/lambda/仿函数封装成一个可调用对象,类似于function,用于在其他线程中处理,我们可以通过packaged_task获取其相关联的future对象,通过get_futrue方法获得。
  • packaged_task是禁止进行拷贝的,如果按引用捕获,当这些变量是局部变量,这些变量可能在线程执行时已经销毁,导致访问错误,因此这里建议使用智能指针来管理packaged_task对象。
  • 关于线程执行函数的选择:lambada表达式相较于普通函数具有储存额外捕获变量的好处,在需要对多变量操作的情况下比较方便直接捕获需要操作的变量即可,其实lambda表达式在运行时编译器会将其当作一个匿名类型的类对象,内部的成员变量就是其捕获的变量,类内部有一个operator()运算符重载函数。
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
#include <memory>
using namespace std;

int Add(int num1, int num2)
{
	return num1 + num2;
}

int main()
{
	auto task = make_shared<packaged_task<int(int, int)>>(Add);//封装任务
	thread([task]() {(*task)(1,0);}).detach();
	future<int> result_future = task->get_future();//获取与任务关联的future对象
	int ret = result_future.get();
	cout << ret;
	return 0;
}

std::promise

  • 就是一个模板类,是对于异步任务结果的封装,在使用的时候,先实例化一个promise对象,通过promise对象获取其关联的future对象,在任意位置给promise设置数据,就可以通过其关联的future获取到设置的数据。注意:std::promise::set_value()通常只能设置一次,重复设置会抛出std::future_error异常。
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
#include <memory>
#include <functional>
using namespace std;

int Add(int num1, int num2)
{
	return num1 + num2;
}

int main()
{
	function<int(int, int)> func(Add);
	auto result_promise = make_shared<promise<int>>();
	future<int> result_future = result_promise->get_future();
	thread([result_promise, func]() {
		int value = func(1, 7);
		this_thread::sleep_for(chrono::seconds(2));
		result_promise->set_value(value);}).detach();
	int ret = result_future.get(); //阻塞等待result_promise被设置数据
	cout << ret;
	return 0;
}
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐