std::future其他的成员函数wait_for()

wait_for() 返回的是一个枚举类型 std::future_status

std::future_status 是枚举类型,表示异步任务的执行状态。类型的取值有

  1. std::future_status::timeout : 表示线程还没执行完
  2. std::future_status::ready :表示线程在规定时间内完成
  3. std::future_status::deferred : 表示 std::async() 使用了 std::launch::deferred

wait_for() 用于卡住当前流程,等待 std::async() 的异步任务运行一段时间,然后返回其状态 std::future_status。如果 std::async() 的参数是 std::launch::deferred(延迟执行),则不会卡住主流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// std::future<int> result = std::async(std::launch::deferred, mythread);
std::future<int> result = std::async(mythread);
std::future_status status = result.wait_for(std::chrono::seconds(1)); // 等待1秒去执行子线程

if(status == std::future_status::timeout)
{
// 超时,表示线程还没执行完
cout << "Time out!" << endl;
}else if(status == std::future_status::ready) // 表示线程成功返回
{
cout << "Thread successfully completed! return value" << endl;
cout << result.get() << endl;
}else if(status == std::future_status::deferred)
{
// 如果 async 的第一个参数被设置为 std::lauch::deferred,则条件成立
cout << "Thread is delayed" << endl;
cout << result.get() << endl;
}

我么也可以通过 wait_for() 来判断系统通过 std::async(mythread) 创建的异步任务是否创建了线程

如果进入 else if(status == std::future_status::deferred) 则表示系统资源紧张而直接在主线程中调用了线程入口函数 mythread()

std::shard_future类模板

std::futurestd::shared_future 不同

  1. std::futureget() 成员函数是转移数据,也就是说采用了移动语义

    我们可以使用 result 中的 valid() 函数来判断是否还有值

    1
    2
    3
    4
    5
    std::future<int> result = std::async([]{
    return 4;
    });
    result.get();
    bool isget = result.valid();

    isget == false 则说明 result 没有值

  2. std::shared_futureget() 成员函数是复制数据

std::atomic原子操作

原子操作概念引出范例

两个线程同时进行加法运算,导致最后结果不准确,因为线程的不断切换中会打断代码导致没运行完整
通过互斥量来进行加锁解锁才会让加法计算运行准确
原子操作可以理解为:不需要用到互斥量加锁(无锁)技术的多线程并发编程方式
也就是说:是在多线程中 不会被打断的程序执行片段,原子操作,效率上比互斥量更胜一筹
互斥量的加锁一般是针对一个代码段(多行代码),二原子操作针对的一般都是一个变量,而不是一个代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int g_i = 0;	// 定义一个全局变量
void mythread()
{
for(int i = 0 ; i < 100000 ; i++)
{
g_i++;
}
}

int main()
{
thread t1(mythread);
thread t2(mythread);
t1.join();
t2.join();
cout << "g_i = " << g_i << endl;
}

按理来说 g_i 应该为 200000 ,但因为线程的切换导致不会加到 200000,且每次结果都不会相同

image-20211119220525072

std::atomic

std::atomic 是一个类模板,用于封装某个类型的值,有两种初始化方式

1
2
std::atomic<int> a = {0};	// 初始化
std::atomic<int> a(0); // 初始化

原子操作,一般都是指“不可分割的操作”;也就是说这种操作状态要么是完成的,要么是没完成的,不可能出现半完成状态

一般atomic原子操作,针对++,--,+=,-=,&=,|=,^=是支持的,其他操作不一定支持。

补充

1
2
std::atomic<int> atm = 0;
cout << atm << endl;

这里只有读取 atm 是原子操作,但是整个这一行代码 cout << atm << endl; 并不是原子操作,导致最终显示在屏幕上的值是一个“曾经值”。

1
2
std::atomic<int> atm = 0;
auto atm2 = atm; //不可以

这种拷贝初始化不可以,会报错。

1
atomic<int> atm2(atm.load());

load():以原子方式读 atomic 对象的值。

1
atm2.store(12);

store():以原子方式写入内容

原子操作实质上是:不允许在进行原子对象操作时进行CPU的上下文切换。