[英]why is moving the definition of the operator() from .h to .cpp file causing a data race?
在我的代碼中,我具有以下頭文件:
Global.h:
#ifndef GLOBAL_H_
#define GLOBAL_H_
#include <mutex>
namespace
{
std::mutex outputMutex;
}
#endif
Test.h:
#ifndef TEST_H_
#define TEST_H_
#include"Global.h"
#include<string>
#include<iostream>
class TestClass
{
std::string name;
public:
TestClass(std::string n):name{n}{}
void operator()()
{
for (int i=0;i<30;++i)
{
std::lock_guard<std::mutex> lock(outputMutex);
std::cout<<name<<name<<name<<name<<name<<name<<name<<std::endl;
}
}
};
#endif
實際上,Test2.h等於Test1.h,只包含一個名為“ TestClass2”的類,而不是“ TestClass” 。 我的main.cpp看起來像這樣:
#include<iostream>
#include <thread>
#include "Global.h"
#include "Test.h"
#include "Test2.h"
using namespace std;
int main()
{
TestClass obj1("Hello");
TestClass2 obj2("GoodBye");
thread t1(obj1);
thread t2(obj2);
t1.join();
t2.join();
}
如果我像這樣運行程序,則會得到預期的輸出:
你好你好你好你好你好你好你好你好
要么
GoodByeGoodByeGoodByeGoodByeGoodByeGoodByeGoodBye
到現在為止還挺好。 但是當我將Test.h和Test2.h的()運算符的定義放在源文件Test.cpp和Test2.cpp中時:
(Test.cpp,與Test2.cpp相同):
#include "Test.h"
#include"Global.h"
void TestClass::operator()()
{
for (int i=0;i<30;++i)
{
std::lock_guard<std::mutex> lock(outputMutex);
std::cout<<name<<name<<name<<name<<name<<name<<name<<std::endl;
}
}
並因此從頭文件中刪除該定義: void operator()();
我突然開始偶爾得到這樣的輸出:
GoodByeHelloGoodByeHelloGoodByeHelloGoodByeHelloGoodByeHelloGoodByeHelloGoodByeHello
我不知道為什么互斥量變量outputMutex
的鎖定不再起作用,但是我認為它與正在創建的變量的兩個版本有關,但是我很想得到專業的解釋。 我在Cygwin中使用Eclipse。
這是未定義行為和匿名名稱空間的混合。
首先這個:
namespace {
std::mutex outputMutex;
}
這是一個包含互斥量outputMatrix
的匿名名稱空間。 每個源文件中都有一個不同的outputMatrix
,因為它具有不同的名稱。
這就是匿名名稱空間的作用。 可以將它們視為“在此處為構建此文件的每個cpp文件生成唯一的guid”。 它們旨在防止鏈接時符號沖突。
class TestClass {
std::string name;
public:
// ...
void operator()() {
// ...
}
};
這是一個(隱式) inline
TestClass::operator()
。 它的主體在每個編譯單元中進行編譯。 通過ODR ,每個編譯單元中的主體必須相同 ,否則您的程序格式不正確,無需進行診斷。 (在類定義inline
定義的方法是隱式inline
,包含所有這些包)。
它使用來自匿名名稱空間的令牌。 該標記在每個編譯單元中都有不同的含義。 如果有多個編譯單元,則結果為格式錯誤的程序,無需診斷; C ++標准對其行為沒有任何限制1 。
在這種特殊情況下,從TestClass
和TestClass2
為operator()
選擇了相同的編譯單元。 因此它使用了相同的互斥量。 這是不可靠的。 局部重建可能會導致其改變,或者說月相。
當您將其放入自己的.cpp
文件時,它不再隱式inline
。 僅存在一個定義,但它們在單獨的編譯單元中。
這兩個不同的編譯單元獲得了不同的outputMatrix
互斥量。
1違反該特定規則的最常見影響是鏈接器根據任意標准(可以在構建之間進行更改!)選擇一個實現,而靜默丟棄其余實現。 這不好,因為對構建過程進行無害的更改(添加更多的內核,部分構建等)可能會破壞您的代碼。 不要違反“內聯函數在任何地方都必須具有相同的定義”規則。 這只是最常見的症狀。 您不保證會有任何明智的事情發生。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.