繁体   English   中英

如何使用 std::jthread 安全地注册 std::stop_callback

[英]how to safely register std::stop_callback with std::jthread

我对如何安全地为jthread注册回调有点困惑。 您需要token ,这意味着您需要在创建jthread之后进行注册,这意味着回调将在jthread之前被销毁。 在下面的示例中, cb5cb6显然在 jthread 的jthread启动之前被销毁,因此它们会自动注销自己并且永远不会执行。 相反, cb1cb2jthread销毁后被显式销毁,因此可以保证它们作为 dtor 请求停止的副作用而执行。 现在令人困惑的部分是我找不到任何保证cb3cb4可以执行的保证。 例如,我找不到任何东西说请求停止会自动更改设置停止标志并执行所有回调。 另一方面,我查看了request_stopImplementation:223 ,似乎它执行以下操作

  • 拿锁
  • 设置停止标志并获取最后注册的停止回调
  • 释放锁
  • 运行回调
  • 释放二进制信号量(该回调的析构函数等待)
  • 尝试重新获取锁以执行下一个回调

现在终于到了这个问题,根据上述, cb3cb4的执行正在与它们的析构函数竞争(至少cb3 ,因为cb4将被选择在用于设置停止标志的同一锁下执行,但又一次我找不到某处提到的cb4的保证)。 那么如何在不显式调用request_stop的情况下正确使用stop_callbackjthread呢? 您不能在之后注册回调,因为它们会像cb5cb6一样在之前被销毁,并且您不能在线程中注册它们,因为它们不能保证像cb3cb4那样执行,而且我不认为它打算给它们以cb1cb2的复杂方式延长使用寿命

#include <chrono>
#include <iostream>
#include <stop_token>
#include <thread>

int main(int argc, const char * const * const argv)
{
    using namespace std::chrono_literals;

    const auto _cb1 = []() -> void {
        std::cout << "cb1\n";
        std::this_thread::yield();
        std::this_thread::sleep_for(10s);
    };

    const auto _cb2 = []() -> void {
        std::cout << "cb2\n";
        std::this_thread::yield();
        std::this_thread::sleep_for(10s);
    };

    using CB1 =
        decltype(std::stop_callback(std::declval<std::stop_token>(), _cb1));
    using CB2 =
        decltype(std::stop_callback(std::declval<std::stop_token>(), _cb2));

    std::byte storage1[sizeof(CB1)];
    std::byte storage2[sizeof(CB2)];

    const CB1 * cb1 = nullptr;
    const CB2 * cb2 = nullptr;

    {

        std::jthread worker([](const std::stop_token & stop_token) {
            std::stop_callback cb3(stop_token, [] {
                std::cout << "cb3\n";
                std::this_thread::yield();
                std::this_thread::sleep_for(10s);
            });
            std::stop_callback cb4(stop_token, [] {
                std::cout << "cb4\n";
                std::this_thread::yield();
                std::this_thread::sleep_for(10s);
            });

            while (!stop_token.stop_requested())
            {
            }
        });


        cb1 = new (&storage1) std::stop_callback(worker.get_stop_token(), _cb1);

        cb2 = new (&storage2) std::stop_callback(worker.get_stop_token(), _cb2);


        std::stop_callback cb5(worker.get_stop_token(), [] {
            std::cout << "cb5\n";
            std::this_thread::yield();
            std::this_thread::sleep_for(10s);
        });

        std::stop_callback cb6(worker.get_stop_token(), [] {
            std::cout << "cb6\n";
            std::this_thread::yield();
            std::this_thread::sleep_for(10s);
        });

        std::this_thread::sleep_for(2s);
    }

    cb1->~CB1();
    cb2->~CB2();

    return 0;
}

该标准直接指出

返回 true 的request_stop调用与返回 true 的关联stop_tokenstop_source stop_requested上的 stop_requested 调用同步。

这意味着对返回truestop_requested的调用“发生在”任何返回truerequest_stop之后。 因此,您的while循环无法退出,直到对request_stop的调用实际上将true返回给某个线程。 更重要的是,在这样的调用返回之前它不能退出。

request_stop 明确声明为:

如果发出请求,则同步调用关联的stop_callback对象注册的回调。

被“同步”调用意味着:这个 function 要么在请求停止的线程上调用它们,要么与它们被调用的任何线程同步。 主要的一点是,在这些回调完成之前,这个 function 不会返回。

并且在此 function 返回之前, stop_requested不会返回true ,如前所述。

所以不存在数据竞赛。 在线程退出之前,回调不会被销毁。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM