簡體   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