我需要保护一段代码免于在协程中同时执行。防止多线程环境中的并发执行很简单,只需使用std::lock_guard类模板。然而,我的协程是从单个线程调用的,因此该解决方案不适用。
以下是我想要完成的(伪)代码:
future<http_response> send_req_async(http_request req) {
while (true) {
// Attempt to send an HTTP request
auto const& access_token{ token_store::access_token() };
auto const response{ co_await impl::send_req_async(req, access_token) };
if (response.status_code() == http_code::ok) {
co_return response;
}
// Attempt to refresh access token
if (response.status_code() == http_code::unauthorized) {
// The following scope needs to be guarded against concurrent execution.
// To guard against concurrent execution from multiple threads I would use:
// lock_guard<mutex> guard(refresh_token_mutex);
if (access_token != token_store::access_token()) {
continue;
}
auto const& token{ co_await refresh_token(token_store::refresh_token()) };
token_store::save_access_token(token);
// End of section that needs to be guarded.
}
}
}
该代码旨在允许并行发出多个请求,同时仅允许尝试刷新过期访问令牌的单个协程调用。理想情况下,解决方案应该在令牌刷新操作正在进行时暂停并发协程调用,并在之后自动恢复(即与std::lock_guard
在多线程环境中)。
协程机制或 C++ 标准库中是否内置了任何东西,可以让我以干净的方式实现这一点,还是我必须自己推出?
注意:我使用的是 Visual Studio 2017 15.7.2,因此您可以假设完全支持 C++17 及其 Coroutine TS 实现。
C++ 或标准库没有提供任何基础设施来获取所需的功能。但是,那协程TS提供构建块来实现co_await
-able 异步互斥体。
总体思路是实现一个等待,在评估时尝试获取合适的互斥体await_suspend
表达。如果无法获取锁,则协程将被挂起并添加到等待者队列中,否则立即继续执行(保持锁)。
互斥体'unlock
方法从队列中恢复等待者,除非等待者队列为空。
网络上有预先构建的解决方案。我和刘易斯·贝克一起去了async_mutex实施的原因有很多:
- 没有外部或内部依赖。只需将编译单元和头文件放入您的项目中即可完成。
- 锁由协程拥有,而不是线程拥有。该实现允许协程在不同的线程上恢复。
- 这是一个无锁的实现。
该实现的使用与std::lock_guard
:
#include <cppcoro/async_mutex.hpp>
namespace {
cppcoro::async_mutex refresh_mutex;
}
future<http_response> send_req_async(http_request req) {
while (true) {
// Attempt to send an HTTP request
auto const& access_token{ token_store::access_token() };
auto const response{ co_await impl::send_req_async(req, access_token) };
if (response.status_code() == http_code::ok) {
co_return response;
}
// Attempt to refresh access token
if (response.status_code() == http_code::unauthorized) {
// The following scope needs to be guarded against concurrent execution.
auto const refresh_guard{ co_await refresh_mutex.scoped_lock_async() };
if (access_token != token_store::access_token()) {
continue;
}
auto const& token{ co_await refresh_token(token_store::refresh_token()) };
token_store::save_access_token(token);
// refresh_guard falls out of scope, unlocking the mutex.
// If there are any suspended coroutines, the oldest one gets resumed.
}
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)