C20来了强大的协程库cppcor

C++20

C++20:到底有没有使用协程进行线程同步?答案是没有!条件变量是线程同步的经典做法,例如在发送者/接收者,或者生产者/消费者模型的工作流程中。条件变量则具有很大的设计缺陷,它们可能在没有通知的情况下被调用(虚假唤醒)或者可能会接到通知。在这两种情况下,我们就可能会陷入僵局。本文隆重介绍一下cppcoro库,提供了一种完美的方案:

一种简单的事件机制;没有条件变量的设计缺陷。下面我们来介绍下cppcoro库的使用!(文末附源代码链接)

single_consumer_event

single_consumer_event是一种简单的手动重置事件类型,仅支持一次等待它的协程:

//cppcororProducerConsumer.cpp#includecppcoro/single_consumer_event.hpp#includecppcoro/sync_wait.hpp#includecppcoro/task.hpp#includefuture#includeiostream#includestring#includethread#includechronocppcoro::single_consumer_eventevent;cppcoro::taskconsumer(){autostart=std::chrono::high_resolution_clock::now();co_awaitevent;//挂起,直到某些线程调用event.setautoend=std::chrono::high_resolution_clock::now();std::chrono::durationdoubleelapsed=end-start;std::coutConsumerwaitedelapsed.count()seconds.std::endl;co_return;}//生产者voidproducer(){usingnamespacestd::chrono_literals;std::this_thread::sleep_for(2s);event.set();//恢复消费者}intmain(){std::coutstd::endl;autocon=std::async([]{cppcoro::sync_wait(consumer());});//(1)autoprod=std::async(producer);//(2)con.get(),prod.get();std::coutstd::endl;}该代码应该是不言自明的。使用者(1)和生产者(2)在其线程中运行。调用cppcoro::sync_wait(consumer())用作顶层任务,因为主函数不能是协程。该呼叫将等待,直到协程消费者完成。协程消费者在调用co_await事件之前等待,直到有人调用event.set()为止,函数生产者在两秒钟的休眠后发送此事件。

是不是得多亏了cppcoro库,可以取消线程。

网络

呼叫者和被呼叫者与cppcoro::cancellation_token通信。得到取消请求的函数的被调用者可以有如下两种方式进行响应:

定期轮询以取消请求。为此,cppcoro::cancellation_token支持两个成员函数:is_cancellation_requested和throw_if_cancellation_requested。注册在取消请求的情况下执行的回调。如下示例:

//cppcoroCancellation.cpp#includechrono#includeiostream#includefuture#includecppcoro/cancellation_token.hpp#includecppcoro/cancellation_source.hppusingnamespacestd::chrono_literals;intmain(){std::coutstd::endl;cppcoro::cancellation_sourcecanSource;cppcoro::cancellation_tokencanToken=canSource.token();//(1)autocancelSender=std::async([canSource]{std::this_thread::sleep_for(2s);canSource.request_cancellation();//(3)std::coutcanSource.request_cancellation()std::endl;});autocancelReceiver=std::async([canToken]{while(true){std::coutWaitforcancellationrequeststd::endl;std::this_thread::sleep_for(ms);if(canToken.is_cancellation_requested())return;//(2)}});cancelSender.get(),cancelReceiver.get();std::coutstd::endl;}在(1)中的cancellation_token,由cancellation_source创建。调用方cancelSender获取/取消源canSource,而被调用方cancelReceiver获取/取消令牌。被叫方永久轮询取消请求(2),该请求在两秒钟后由呼叫者通过cal.call.request_cancellation(3)发送。

以上示例表明:

取消动作对于呼叫方和被呼叫方是协同的。如果被呼叫方忽略该取消请求,则不会发生任何事情。C++20改进了std::thread:std::jthread,std::jthread自动加入其析构函数中,并且可以通过中断令牌来实现中断。cppcoro是支持互斥。

async_mutex

cppcoro::async_mutex类的互斥锁是一种同步机制,用于保护共享数据免受多个线程同时访问。

//cppcoroMutex.cpp#includecppcoro/async_mutex.hpp#includecppcoro/sync_wait.hpp#includecppcoro/task.hpp#includeiostream#includethread#includevectorcppcoro::async_mutexmutex;intsum{};//(2)cppcoro::taskaddToSum(intnum){cppcoro::async_mutex_locklockSum=co_awaitmutex.scoped_lock_async();//(3)sum+=num;}//(4)intmain(){std::coutstd::endl;std::vectorstd::threadvec(10);//(1)for(autothr:vec){thr=std::thread([]{for(intn=0;n10;++n)cppcoro::sync_wait(addToSum(n));});}for(autothr:vec)thr.join();std::coutsum:sumstd::endl;std::coutstd::endl;}

在(1)中创建十个线程。每个线程将数字0到9共享加到(2)上。函数addToSum是协程,协程在表达式co_awaitMutex.scoped_lock_async(3)中等待,直到获取互斥量为止。等待互斥量的协程不会被阻止而是被挂起。前一个锁持有者在其解锁呼叫中恢复等待的协程。顾名思义,互斥锁一直锁定到作用域的末尾(4)。

更多应用

借助功能cppcoro::when_all,我们不仅可以等待一个,而且可以等待更多协程。在后续的文章中,我尝试把玩更多cppcoro的功能,构建功能强大的工作流程。

传送门

源码:


转载请注明:http://www.aierlanlan.com/grrz/3589.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了