簡體   English   中英

使用對象生存期運行線程

[英]Run thread with object lifetime

在我的A類中A有一個線程在對象生存期內一直運行。 現在,我有一個布爾成員變量,每個循環都會對其進行檢查,在析構函數中,此變量設置為false。

class A {
public:
    A() : mRun(true) {
        mThread = std::thread(&A::DoWork(), this);
    }

    ~A() {
        mRun = false;
    }

private:
    bool        mRun;
    std::thread mThread;

    void DoWork() {
        while (mRun) {
            ...
        }
    }
};

是否可以安全地使用while(true) 我讀到關於線程破壞的消息,它們將被終止。

“可以安全地使用while(true)嗎?”

是的(假設while(true)實際上意味着while (mRun) )。 您需要確保該mRun成員安全,以便從不同線程進行並發讀/寫訪問。 最簡單的方法是使用std::atomic<>值,如下所示:

class A {
public:
    A() : mRun(true) {
        mThread = std::thread(&A::DoWork(), this);
    }

    ~A() {
        mRun = false; // <<<< Signal the thread loop to stop
        mThread.join(); // <<<< Wait for that thread to end
    }

private:
    std::atomic<bool>        mRun; // Use a race condition safe data 
                                   // criterium to end that thread loop
    std::thread mThread;

    void DoWork() {
        while (mRun == true) {
            ...
        }
    }
};

其中mRun == true應該回mRun == true std::atomic::operator T()函數。

有關如何使OP的問題線程安全的直接說明,請閱讀第一段。 其余部分描述了執行退出條件的另一種方法。

是的,您可以安全地在代碼中使用while(mRun) 為了使您現有的示例線程安全,請調用mthread.join()以便在線程仍在運行時不讓其執行析構函數。


控制線程執行的另一種方法是使用異常和異常處理程序來逃避線程循環。 這是通過在循環外部使用try catch語句來完成的。 這是boost::thread使用的方法,因此在您的方法中,您將調用my_thread.interrupt_check() ,這將引發異常。 這樣可以解開堆棧,並銷毀堆棧上的所有對象。 但是您必須小心使用此方法。 如果線程中未捕獲到異常,則程序將調用std::terminate ,整個程序將停止。 此方法的示例如下所示:

void interrupt_check();

class interruptible_thread
{
private:
    static thread_local std::atomic<bool>* interrupt_flag;
    friend void interrupt_check();

    std::atomic<bool> flag;
    std::thread thread_obj;

    struct interrupt_exception { };

public:
    template<typename _Fn, typename... _Args>
    interruptible_thread(_Fn& function, _Args& arguments) :
        flag(false),
        thread_obj([](std::atomic<bool>* flag_var, _Fn&& function, _Args&& arguments)
            { interrupt_flag = flag_var; 
              try
              {
                  function(std::forward<_Args>(arguments));
              } catch (interrupt_exception& e) { }
            },
            &interrupt_flag,
            std::forward<_Fn>(function),
            std::forward<_Args>(arguments))
    { }

    void interrupt() { flag = true; }

    ~interruptible_thread() { flag = false; interrupt(); thread_obj.join(); }
}

void interrupt_check()
{
    if(!interruptible_thread::interrupt_flag)
        throw interruptible_thread::interrupt_exception;
}

此方法的優點是可以正確執行析構函數,並且可以在調用方希望時終止線程。 缺點是,如果線程不進行檢查,那么這就是std::thread ,開銷更大。

另請注意,如果在線程仍可連接的情況下銷毀了std::thread對象,則會調用std::terminate 我想這不是將線程包裝在對象中的意圖。

您必須記住,由於對象被銷毀,mRun僅會設置為false。 由於它是在自己的線程上,所以這不是一個好主意。 一個更好的主意是使用設置的全局布爾值,此類可以在該布爾值上使用原子操作,以防止兩個線程向其寫入數據(例如,線程A將其設置為FALSE,而線程B則設置為BEHIND,並且;不管出於何種原因-將其設置為TRUE(壞主意...),以這種方式,當對象被破壞時,您可以檢測到它不再存在,並且不嘗試訪問它。

當我做這樣的事情時,我傾向於將線程本身的一個實例傳遞給我的RUN函數,因此我可以像這樣訪問它

while(threadInstance->mRun)

但這可能對您不起作用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM