c++ 在没有同步的情况下,co_await和恢复另一个线程上的协程安全吗?

thtygnil  于 2023-02-14  发布在  其他
关注(0)|答案(2)|浏览(148)

cppreference有一个co_await的例子,它在另一个线程上恢复协程。
但是在没有同步的情况下这样做安全吗?
具体来说,resume_on_new_thread()从一个线程开始,然后在co_await之后用另一个线程访问相同的协程“帧”继续。通常,如果你从两个线程访问相同的数据,你需要同步。

#include <coroutine>
#include <iostream>
#include <stdexcept>
#include <thread>
 
auto switch_to_new_thread(std::jthread& out)
{
    struct awaitable
    {
        std::jthread* p_out;
        bool await_ready() { return false; }
        void await_suspend(std::coroutine_handle<> h)
        {
            std::jthread& out = *p_out;
            if (out.joinable())
                throw std::runtime_error("Output jthread parameter not empty");
            out = std::jthread([h] { h.resume(); });
            // Potential undefined behavior: accessing potentially destroyed *this
            // std::cout << "New thread ID: " << p_out->get_id() << '\n';
            std::cout << "New thread ID: " << out.get_id() << '\n'; // this is OK
        }
        void await_resume() {}
    };
    return awaitable{&out};
}
 
struct task
{
    struct promise_type
    {
        task get_return_object() { return {}; }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() {}
    };
};
 
task resuming_on_new_thread(std::jthread& out)
{
    std::cout << "Coroutine started on thread: " << std::this_thread::get_id() << '\n';
    co_await switch_to_new_thread(out);
    // awaiter destroyed here
    std::cout << "Coroutine resumed on thread: " << std::this_thread::get_id() << '\n';
}
 
int main()
{
    std::jthread out;
    resuming_on_new_thread(out);
}

考虑函数是否有数据:

task resuming_on_new_thread(std::jthread& out)
{
    int data = 0; // Store some data using thread A.
    std::cout << "Coroutine started on thread: " << std::this_thread::get_id() << '\n';
    co_await switch_to_new_thread(out);
    // awaiter destroyed here
    std::cout << "Coroutine resumed on thread: " << std::this_thread::get_id() << '\n';
    std::cout << data << '\n'; // Access data with thread B. **Is this safe?**
}
ldfqzlk8

ldfqzlk81#

是的,它是安全的。std::jthread的构造函数是synchronization。

r1zk6ea1

r1zk6ea12#

这是安全的。请记住,当协程挂起时,它会将控制权返回给调用者。因此,只有恢复协程的线程才能继续执行协程主体。在您的示例中,协程从主线程开始,然后挂起,然后在out上恢复。
只有当您尝试从 *2个不同的 * 线程恢复 * 相同的 * 协程时,才会发生数据争用,这是未定义的行为,如std::coroutine_handle<Promise>::resume()here的注解中所述
如果*this不引用挂起的协程[...],则该行为未定义。并发恢复协程可能导致数据争用。

相关问题