async
在 C++ 中,async
关键字用于实现异步编程,它允许你定义异步操作,这些操作可以在后台执行,而不会阻塞当前线程。这是 C++11 引入的特性,与 std::async
函数和 std::future
类一起使用。与thread函数模板的区别在于async可以有返回值,thread无返回值。
简单入门
// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>
#include <list>
using namespace std;
int mythread()
{
cout << "func " << " threadid = " << this_thread::get_id() << " start " << endl << endl;
std::chrono::milliseconds dura(3000);
std::this_thread::sleep_for(dura);
cout << "func " << " threadid = " << this_thread::get_id() <<" end " << endl << endl;
return 5;
}
int main()
{
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
std::future<int> ret = std::async(mythread);
cout << ret.get() << endl; //堵塞获取值,注意:get函数只能调用一次,不能多次调用
//注意:如果不写get,程序会等子线程退出后在自行退出
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
return 0;
}
中级过度
// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>
#include <list>
using namespace std;
class A
{
public:
int mythread(int myint)
{
cout << "func " << " threadid = " << this_thread::get_id() << " start " << endl << endl;
cout << "myint = " << myint << endl;
std::chrono::milliseconds dura(3000);
std::this_thread::sleep_for(dura);
cout << "func " << " threadid = " << this_thread::get_id() << " end " << endl << endl;
return 5;
}
};
int main()
{
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
A a;
int num = 100;
std::future<int> ret = std::async(&A::mythread,&a,num);//传入可调用对象 &a == std::ref(a)都是引用传递 相当于传递了this指针。
cout << ret.get() << endl; //堵塞获取值
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
return 0;
}
高手使用
在 C++ 中,std::async
函数用于启动一个异步任务,并且可以与 std::launch
策略一起使用来控制任务的执行方式。std::launch
是 std::launch
枚举的一个值,它指示 std::async
以延迟(deferred)的方式执行给定的可调用对象。
当你使用 std::launch::deferred
时,std::async
将立即返回一个 std::future
对象,但不会立即执行传入的函数或函数对象。相反,函数的执行被延迟,直到你显式地请求结果,通常是通过调用 std::future::get
方法。这允许你在不阻塞当前线程的情况下启动异步操作。
意思就是说在调用get的函数,才开始执行程序,且是在主线程执行的,不调用的话函数不会运行。
// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>
#include <list>
using namespace std;
class A
{
public:
int mythread(int myint)
{
cout << "func " << " threadid = " << this_thread::get_id() << " start " << endl << endl;
cout << "myint = " << myint << endl;
std::chrono::milliseconds dura(3000);
std::this_thread::sleep_for(dura);
cout << "func " << " threadid = " << this_thread::get_id() << " end " << endl << endl;
return 5;
}
};
int main()
{
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
A a;
int num = 100;
std::future<int> ret = std::async(std::launch::async|std::launch::deferred,&A::mythread,&a,num);//传入可调用对象 &a == std::ref(a)都是引用传递 相当于传递了this指针。
cout << ret.get() << endl; //堵塞获取值
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
return 0;
}
future
在 C++11 及以后的版本中,std::future
是一个模板类,它提供了一种方式来获取异步操作的结果。当你启动一个异步操作时,例如使用 std::async
或者在新线程中执行一个任务,你可以获得一个 std::future
对象,它代表了异步操作的最终结果。
以下是 std::future
的一些关键特性和用法:
-
获取结果:使用
get()
方法来获取异步操作的结果。这将阻塞当前线程直到异步操作完成。 -
检查状态:你可以使用
valid()
方法来检查std::future
对象是否包含一个结果。 -
等待结果:使用
wait()
或wait_for()
方法来等待异步操作完成,而不必阻塞当前线程。 -
移动语义:
std::future
对象只能被移动,不能被复制。 -
共享状态:
std::future
对象共享其关联的异步操作的状态。这意味着你可以有多个std::future
对象指向同一个异步操作。 -
异常处理:如果异步操作抛出异常,
get()
方法将重新抛出相同的异常。 -
重置:一旦
get()
被调用,std::future
对象将被重置,你不能再从同一个std::future
对象获取结果。
简单入门
// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>
#include <list>
using namespace std;
class A
{
public:
int mythread(int myint)
{
cout << "func " << " threadid = " << this_thread::get_id() << " start " << endl << endl;
cout << "myint = " << myint << endl;
std::chrono::milliseconds dura(2000);
std::this_thread::sleep_for(dura);
cout << "func " << " threadid = " << this_thread::get_id() << " end " << endl << endl;
return 5;
}
};
int main()
{
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
A a;
int num = 100;
std::future<int> ret = std::async(std::launch::async, &A::mythread, &a, num);//传入可调用对象 &a == std::ref(a)都是引用传递 相当于传递了this指针。
std::future_status status = ret.wait_for(std::chrono::milliseconds(1000)); //等待一秒钟。
if (status == std::future_status::timeout)
{
cout << "超时了" << endl;
}
else if (status == std::future_status::deferred) //延迟,async启动参数是 deferred就会走这个。
{
cout << "延时了" << endl;
}
else if (status == std::future_status::ready)
{
cout << "已经完成了" << endl;
}
//cout << ret.get() << endl; //堵塞获取值 get内部使用了move移动语义,使用后就无值了
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
return 0;
}
中级过度
// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>
#include <list>
using namespace std;
int mythread()
{
cout << "=============func========================== " << " threadid = " << this_thread::get_id() << endl << endl;
return 100;
}
int main()
{
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
future<int>fut = std::async(mythread);//我们一般叫他创建一个异步任务。
//fut.wait_until(std::chrono::seconds(3));
// 设置一个特定的截止时间点
auto now = std::chrono::system_clock::now() + std::chrono::seconds(3);
fut.wait_until(now);
// 等待直到截止时间点
cout << fut.get() << endl;
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
return 0;
}
shared_future
在 C++ 中,std::shared_future
是一个模板类,它提供了一种机制来访问异步操作的结果,类似于 std::future
,不同之处在于允许多个线程等待同一个共享状态。这与仅可移动的 std::future
(因此只有一个实例能指代任何特定的异步结果)形成对比。
std::shared_future
可以通过 std::future
对象隐式转换(参见 std::shared_future
的构造函数),或者通过 std::future::share()
显式转换,无论哪种转换,被转换的那个 std::future
对象自身将失效。
使用 std::shared_future
-
获取
std::shared_future
对象:std::shared_future
对象可以通过std::future
对象隐式转换获得,也可以通过调用std::future::share()
方法显式转换获得。 -
等待结果:与
std::future
一样,std::shared_future
也提供了get()
,wait()
,wait_for()
, 和wait_until()
方法来等待异步操作的结果。 -
异常处理:如果异步操作抛出异常,
get()
方法将重新抛出该异常。 -
共享状态:
std::shared_future
与std::promise
相关联,用于管理和使用std::future
对象。std::shared_future
允许多个线程共享同一个std::promise
或std::packaged_task
的最终结果。
简单使用
// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>
#include <list>
using namespace std;
class A
{
public:
int mythread(int myint)
{
cout << "func " << " threadid = " << this_thread::get_id() << " start " << endl << endl;
cout << "myint = " << myint << endl;
std::chrono::milliseconds dura(2000);
std::this_thread::sleep_for(dura);
cout << "func " << " threadid = " << this_thread::get_id() << " end " << endl << endl;
return 5;
}
};
int main()
{
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
A a;
int num = 100;
std::future<int> ret = std::async(std::launch::async, &A::mythread, &a, num);//传入可调用对象 &a == std::ref(a)都是引用传递 相当于传递了this指针。
std::future_status status = ret.wait_for(std::chrono::milliseconds(1000)); //等待一秒钟。
// std::shared_future<int> shared_ret = ret.share();
std::shared_future<int> shared_ret (std::move(ret) ) ;
if (status == std::future_status::timeout)
{
cout << "超时了" << endl;
}
else if (status == std::future_status::deferred) //延迟,async启动参数是 deferred就会走这个。
{
cout << "延时了" << endl;
}
else if (status == std::future_status::ready)
{
cout << "已经完成了" << endl;
}
cout << shared_ret.get() << endl; //可以获取多次值
cout << shared_ret.get() << endl; //可以获取多次值
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
return 0;
}
package_task
个人感觉,这个类模板在实际工作中没啥用处。
在 C++ 中,std::packaged_task
是一个类模板,它用于包装任何可调用对象(如函数、lambda 表达式、函数对象等),以便它可以被异步调用。它的返回值或抛出的异常被存储在一个共享状态中,可以通过 std::future
对象访问。std::packaged_task
与 std::future
结合使用时,可以轻松管理异步操作。
以下是如何使用 std::packaged_task
的基本步骤:
-
创建
std::packaged_task
对象:你可以创建一个std::packaged_task
对象,并将一个可调用对象(如函数或 lambda 表达式)传递给它。 -
获取
std::future
对象:通过调用get_future()
方法,你可以获取一个std::future
对象,该对象可以用来获取异步操作的结果。 -
启动异步任务:你可以通过在新的线程中调用
std::packaged_task
对象来启动异步任务。 -
等待结果:使用
std::future
对象的get()
方法来等待异步操作完成并获取结果。
简单入门
// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>
#include <list>
using namespace std;
int mythread(int myint)
{
cout << "func " << " threadid = " << this_thread::get_id() << " start " << endl << endl;
cout << "myint = " << myint << endl;
std::chrono::milliseconds dura(3000);
std::this_thread::sleep_for(dura);
cout << "func " << " threadid = " << this_thread::get_id() << " end " << endl << endl;
return 5;
}
int main()
{
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
//std::packaged_task类模板
std::packaged_task<int(int)>mypt(mythread); //把函数mytherad通过package_task包装
std::thread t1(std::ref(mypt),120);
t1.join(); //这里的join不可以省略
//执行完毕
future<int>ret = mypt.get_future(); //获取到future对象。
cout << "fun finish ret = " << ret.get() << endl;
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
return 0;
}
中级过度
// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>
#include <list>
using namespace std;
int mythread(int myint)
{
cout << "func " << " threadid = " << this_thread::get_id() << " start " << endl << endl;
cout << "myint = " << myint << endl;
std::chrono::milliseconds dura(3000);
std::this_thread::sleep_for(dura);
cout << "func " << " threadid = " << this_thread::get_id() << " end " << endl << endl;
return 5;
}
int main()
{
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
//std::packaged_task类模板
std::packaged_task<int(int)>mypt([](int num) {
cout << "func " << " threadid = " << this_thread::get_id() << " start " << endl << endl;
cout << "myint = " << num << endl;
std::chrono::milliseconds dura(3000);
std::this_thread::sleep_for(dura);
cout << "func " << " threadid = " << this_thread::get_id() << " end " << endl << endl;
return 5;
}); //把函数mytherad通过package_task包装
mypt(300); //包装对象可以直接调用。,在主线程执行,未创建线程
//执行完毕
future<int>ret = mypt.get_future(); //获取到future对象。
cout << "fun finish ret = " << ret.get() << endl;
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
return 0;
}
高手使用
// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>
#include <list>
using namespace std;
class A
{
public:
int mythread(int myint)
{
cout << "func " << " threadid = " << this_thread::get_id() << " start " << endl << endl;
cout << "myint = " << myint << endl;
std::chrono::milliseconds dura(3000);
std::this_thread::sleep_for(dura);
cout << "func " << " threadid = " << this_thread::get_id() << " end " << endl << endl;
return 5;
}
};
int mythread(int myint)
{
cout << "func " << " threadid = " << this_thread::get_id() << " start " << endl << endl;
cout << "myint = " << myint << endl;
std::chrono::milliseconds dura(3000);
std::this_thread::sleep_for(dura);
cout << "func " << " threadid = " << this_thread::get_id() << " end " << endl << endl;
return 5;
}
vector<std::packaged_task<int(int)> > mystatsks;
int main()
{
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
//std::packaged_task类模板
std::packaged_task<int(int)>mypt([](int num) {
cout << "func " << " threadid = " << this_thread::get_id() << " start " << endl << endl;
cout << "myint = " << num << endl;
std::chrono::milliseconds dura(3000);
std::this_thread::sleep_for(dura);
cout << "func " << " threadid = " << this_thread::get_id() << " end " << endl << endl;
return 5;
}); //把函数mytherad通过package_task包装
cout << "mypt.valid = " << mypt.valid() << endl;
//mystatsks.push_back( (mypt) ); //默认是调用拷贝构造,此处内部被删除了
mystatsks.push_back(std::move(mypt)); //移动到容器中。 mypt就无效了
cout << "mypt.valid = " << mypt.valid() << endl;
std::packaged_task<int(int)>mypt2;
auto it = mystatsks.begin(); //获取第一个元素
mypt2 = std::move(*it); //移动到mypt2
cout <<"mystsks.size() = "<< mystatsks.size() << endl;
mystatsks.erase(it);
mypt2(123);
std::future<int>ret =mypt2.get_future() ;
cout << "ret = " << ret.get();
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
return 0;
}
promise
在 C++11 及以后的版本中,std::promise
是一个模板类,它提供了一种方式来存储值或异常,并且可以在稍后某个时刻通过 std::future
对象访问这些值或异常。std::promise
通常与 std::future
一起使用,以实现线程间的数据传递或同步。
以下是 std::promise
的一些关键特点和用法:
-
存储值:
std::promise
可以存储一个值,这个值可以是任何类型,包括自定义类型。 -
存储异常:如果
std::promise
存储了一个异常,那么当std::future
对象尝试获取值时,这个异常会被抛出。 -
设置值:一旦
std::promise
被设置(通过set_value
或set_value_at_thread
方法),它就不能再次被设置。如果尝试再次设置,将抛出std::future_error
异常。 -
获取值:
std::future
对象使用get
方法从关联的std::promise
获取值。如果std::promise
存储了异常,get
将抛出这个异常。 -
等待:
std::future
对象可以使用wait
或wait_for
方法等待std::promise
设置值。 -
移动语义:
std::promise
对象可以被移动,但不能被复制。
简单入门
// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>
#include <list>
using namespace std;
void mythread(std::promise<int>& pro, int calc)
{
calc++;
std::chrono::milliseconds durn(1000);
std::this_thread::sleep_for(durn);
int ret = calc;
pro.set_value(ret); //在promise中复制,promise英文是许诺的意思
return;
}
int main()
{
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
#if 0
std::promise<int> prom;
std::thread t1(mythread, ref(prom), 200);
t1.join();
std::future<int> fu = prom.get_future();
cout << "val = " << fu.get() << endl;
#else
std::promise<int> prom;
std::future<void>fu1 = std::async(mythread,ref(prom),200);
std::future<int>furet = prom.get_future();
cout << "val = " << furet.get() << endl;
#endif
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
return 0;
}
高手使用
// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>
#include <list>
using namespace std;
void mythread(std::promise<int>& pro, int calc)
{
cout << "mythread start " << endl;
calc++;
std::chrono::milliseconds durn(1000);
std::this_thread::sleep_for(durn);
int ret = calc;
pro.set_value(ret); //在promise中复制,promise英文是许诺的意思
return;
}
void mythread2(std::future<int>& fut)
{
cout << "mythread2 start " << endl;
int num = fut.get();
cout << "mythread2 = " << num << endl;
}
int main()
{
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
#if 1
std::promise<int> prom;
std::thread t1(mythread, ref(prom), 200);
//t1.join();
t1.detach();
std::future<int> fu = prom.get_future();
//cout << "val = " << fu.get() << endl; //拿不到值就死等,直到拿到值
std::thread t2(mythread2, std::ref(fu));
t2.join();//等线程2执行完毕。
#else
std::promise<int> prom;
std::future<void>fu1 = std::async(mythread,ref(prom),200);
std::future<int>furet = prom.get_future();
cout << "val = " << furet.get() << endl; //拿不到值就死等,直到拿到值
#endif
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
return 0;
}
atomic原子操作
在 C++ 中,原子操作(Atomic Operations)是用于在多线程环境中安全地执行单个变量操作的方法,而不需要使用互斥锁(mutexes)。原子操作可以保证在多线程同时访问和修改共享数据时,数据的完整性和一致性不会受到破坏。
C++11 引入了 <atomic>
头文件,它提供了一组原子类型和函数,用于执行原子操作。原子类型包括 std::atomic_flag
、std::atomic_bool
、std::atomic_char
等,以及针对各种整数类型的原子类型,如 std::atomic<int>
、std::atomic<unsigned long long>
等。
以下是一些常见的原子操作和它们的作用:
-
初始化:
std::atomic_flag
和其他原子类型可以使用构造函数初始化。 -
存储和加载:
store
方法用于将一个值原子地存储到原子对象中,load
方法用于安全地加载原子对象的值。 -
原子交换:
exchange
方法用于原子地交换原子对象中的值。 -
比较并交换:
compare_exchange_weak
或compare_exchange_strong
方法用于原子地比较并交换原子对象中的值,如果当前值与预期值相等,则替换为新值。 -
增加和减少:
fetch_add
、fetch_sub
、fetch_and
、fetch_or
、fetch_xor
等方法用于原子地对原子对象执行增加、减少、逻辑与、逻辑或、逻辑异或等操作。 -
等待通知:
wait
和notify_one
/notify_all
方法用于线程同步,允许线程在某个条件成立之前挂起,并在条件成立时唤醒。
原子操作通常比使用互斥锁有更低的开销,因为它们不需要操作系统介入来管理锁。然而,它们不提供互斥锁那样的复杂同步机制,比如锁的可重入性。
简单使用
// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <future>
#include <list>
using namespace std;
//int g_c = 0; //普通使用会出现线程竞争问题。
// std::atomic<int> g_c = 0; //使用atomic不会不出现问题。
// std::atomic<int>at2(g_c.load()); //可以加载原子操作
// g_c.store(12); //存储值
void mythread()
{
for (int i = 0; i < 100000; i++)
{
//g_c++; //普通用法
//g_c = g_c + 1; //这样写结果会计算错误。
g_c.fetch_add(1, std::memory_order_relaxed); //使用fetch_add取出数据并添加数据
//请注意,std::memory_order_relaxed 是一个内存顺序选项,它确保操作的顺序性。
// C++ + 提供了一系列内存顺序选项,允许开发者根据具体需求选择合适的内存顺序保证。
//memory_order_relaxed, //放宽
//memory_order_consume, //消费
//
//memory_order_acquire, //获得
//memory_order_release, //释放
//memory_order_acq_rel,
//memory_order_seq_cst
}
}
int main()
{
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
thread th1(mythread);
thread th2(mythread);
th1.join();
th2.join();
cout << "g_c = " << g_c << endl;
cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
return 0;
}
其他线程使用可以参数我主页中的 C语言:高级并发操作(线程)文章。