簡體   English   中英

C ++ 11 std :: thread和虛函數綁定

[英]C++11 std::thread and virtual function binding

我遇到了一個奇怪的C ++代碼行為,不確定它是編譯器錯誤還是我的代碼的未定義/未指定的行為。 這是代碼:

#include <unistd.h>
#include <iostream>
#include <thread>

struct Parent {
    std::thread t;

    static void entry(Parent* p) {
        p->init();
        p->fini();
    }

    virtual ~Parent() { t.join(); }

    void start() { t = std::thread{entry, this}; }

    virtual void init() { std::cout << "Parent::init()" << std::endl; }
    virtual void fini() { std::cout << "Parent::fini()" << std::endl; }
};

struct Child : public Parent {
    virtual void init() override { std::cout << "Child::init()" << std::endl; }
    virtual void fini() override { std::cout << "Child::fini()" << std::endl; }
};

int main() {
    Child c;

    c.start();
    sleep(1); // <========== here is it

    return 0;
}

代碼的輸出如下,這並不奇怪:

Child::init()
Child::fini()

但是,如果函數調用“sleep(1)”被注釋掉,輸出將是:

Parent::init()
Parent::~fini()

在Ubuntu 15.04上測試,gcc-4.9.2和clang-3.6.0都表現出相同的行為。 編譯器選項:

g++/clang++ test.cpp -std=c++11 -pthread

它看起來像一個競爭條件(vtable在線程開始之前沒有完全構造)。 這段代碼是不正確的嗎? 編譯器錯誤? 或者它應該是這樣的?

線程使用子對象,但子對象在連接線程之前被銷毀(因為僅在子對象的銷毀開始后才進行連接)。

Child對象在main結束時被銷毀。 Child析構函數被執行,並有效地調用Parent析構函數,其中Parent基(沒有這樣)和數據成員(線程對象)被銷毀。 當析構函數被調用到基類鏈時,對象的動態類型會在構造期間以相反的順序改變,因此此時對象的類型是Parent

線程函數中的虛擬調用可以在Child析構函數調用之前,重疊或之后發生,並且在重疊的情況下,有一個線程訪問由另一個線程更改的存儲(實際上是vtable指針)。 所以這是未定義的行為。

這是常見的設計問題; 你試圖做的是一個經典的反模式。

Parent不能同時是一個線程管理器,啟動一個線程並等待線程終止:

virtual ~Parent() { t.join(); }

void start() { t = std::thread{entry, this}; }

還有一個線程對象:

virtual void init() { std::cout << "Parent::init()" << std::endl; }
virtual void fini() { std::cout << "Parent::fini()" << std::endl; }

這是兩個截然不同的概念,具有嚴格不兼容的規范。

(並且線程對象通常沒用。)

暫無
暫無

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

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