Derived from this question和related to this question:
如果我在一个线程ä¸æž„é€ ä¸€ä¸ªå¯¹è±¡è€Œç„¶åŽå°†ä¸€ä¸ªå¼•ç”¨/æŒ‡é’ˆä¼ é€’ç»™å¦ä¸€ä¸ªçº¿ç¨‹ï¼Œé‚£ä¹ˆè¯¥çº¿ç¨‹çš„其他线程在没有显å¼é”定/内å˜çš„情况下访问该对象是ä¸å®‰å…¨çš„-barriers?
// thread 1
Obj obj;
anyLeagalTransferDevice.Send(&obj);
while(1); // never let obj go out of scope
// thread 2
anyLeagalTransferDevice.Get()->SomeFn();
或者:是å¦æœ‰ä»»ä½•åˆæ³•çš„æ–¹å¼åœ¨çº¿ç¨‹ä¹‹é—´ä¼ 递数æ®ï¼Œè€Œè¿™äº›çº¿ç¨‹æ²¡æœ‰å¼ºåˆ¶æ‰§è¡Œå…³äºŽçº¿ç¨‹è§¦åŠçš„所有 else 的内å˜æŽ’åºï¼Ÿä»Žç¡¬ä»¶çš„角度æ¥çœ‹ï¼Œæˆ‘认为没有任何ç†ç”±ä¸å¯èƒ½ã€‚
澄清;问题是关于缓å˜ä¸€è‡´æ€§ï¼Œå†…å˜æŽ’åºç‰ç‰ã€‚在线程2的内å˜è§†å›¾åŒ…å«æž„é€ obj
所涉åŠçš„写入之å‰ï¼Œçº¿ç¨‹2是å¦å¯ä»¥èŽ·å–并使用指针?错过引用Alexandrescu(?)“æ¶æ„çš„CPU设计人员和编译器编写者是å¦å¯ä»¥å…±åŒæž„å»ºä¸€ä¸ªæ ‡å‡†çš„ç¬¦åˆç³»ç»Ÿæ¥å®žçŽ°è¿™ä¸€ç›®æ ‡ï¼Ÿâ€
ç”案 0 :(得分:17)
关于线程安全的推ç†å¯èƒ½å¾ˆå›°éš¾ï¼Œè€Œä¸”我ä¸æ˜¯C ++ 11内å˜æ¨¡åž‹çš„专家。但幸è¿çš„æ˜¯ï¼Œä½ çš„ä¾‹åéžå¸¸ç®€å•ã€‚我é‡å†™äº†è¿™ä¸ªä¾‹åï¼Œå› ä¸ºæž„é€ å‡½æ•°æ˜¯æ— å…³ç´§è¦çš„。
问题:以下代ç 是å¦æ£ç¡®ï¼Ÿæˆ–者执行结果å¯ä»¥æœªå®šä¹‰çš„行为?
// Legal transfer of pointer to int without data race.
// The receive function blocks until send is called.
void send(int*);
int* receive();
// --- thread A ---
/* A1 */ int* pointer = receive();
/* A2 */ int answer = *pointer;
// --- thread B ---
int answer;
/* B1 */ answer = 42;
/* B2 */ send(&answer);
// wait forever
ç”案: answer
的内å˜ä½ç½®å¯èƒ½å˜åœ¨æ•°æ®ç«žäº‰ï¼Œå› æ¤æ‰§è¡Œä¼šå¯¼è‡´æœªå®šä¹‰çš„行为。有关详细信æ¯ï¼Œè¯·å‚阅下文。
当然,ç”案å–决于函数send
å’Œreceive
çš„å¯èƒ½å’Œåˆæ³•å®žçŽ°ã€‚我使用以下数æ®æ— 竞争实现。请注æ„,仅使用å•ä¸ªåŽŸåå˜é‡ï¼Œå¹¶ä¸”所有内å˜æ“作都使用std::memory_order_relaxed
。基本上这æ„味ç€ï¼Œè¿™äº›åŠŸèƒ½ä¸ä¼šé™åˆ¶å†…å˜é‡æ–°æŽ’åºã€‚
std::atomic<int*> transfer{nullptr};
void send(int* pointer) {
transfer.store(pointer, std::memory_order_relaxed);
}
int* receive() {
while (transfer.load(std::memory_order_relaxed) == nullptr) { }
return transfer.load(std::memory_order_relaxed);
}
åœ¨å¤šæ ¸ç³»ç»Ÿä¸Šï¼Œçº¿ç¨‹å¯ä»¥æŒ‰ç…§å…¶ä»–线程看到的ä¸åŒé¡ºåºæŸ¥çœ‹å†…å˜æ›´æ”¹ã€‚æ¤å¤–,编译器和CPU都å¯ä»¥åœ¨å•ä¸ªçº¿ç¨‹å†…é‡æ–°æŽ’åºå†…å˜æ“作以æ高效率 - è€Œä¸”ä»–ä»¬ä¸€ç›´è¿™æ ·åšã€‚使用std::memory_order_relaxed
的原åæ“作ä¸å‚与任何åŒæ¥ï¼Œä¹Ÿä¸ä¼šå¼ºåŠ 任何排åºã€‚
在上é¢çš„例åä¸ï¼Œå…许编译器é‡æ–°æŽ’åºçº¿ç¨‹Bçš„æ“作,并在B1之å‰æ‰§è¡ŒB2ï¼Œå› ä¸ºé‡æ–°æŽ’åºå¯¹çº¿ç¨‹æœ¬èº«æ²¡æœ‰å½±å“。
// --- valid execution of operations in thread B ---
int answer;
/* B2 */ send(&answer);
/* B1 */ answer = 42;
// wait forever
C ++ 11定义了一个数æ®ç«žäº‰å¦‚下(N3290 C ++ 11 Draft):“程åºçš„执行包å«æ•°æ®ç«žäº‰ï¼Œå¦‚果它在ä¸åŒçš„线程ä¸åŒ…å«ä¸¤ä¸ªå†²çªçš„动作,其ä¸è‡³å°‘有一个ä¸æ˜¯ atomic ,而å‘生在å¦ä¸€ä¸ªä¹‹å‰ã€‚任何æ¤ç±»æ•°æ®ç«žäº‰éƒ½ä¼šå¯¼è‡´æœªå®šä¹‰çš„行为。“术è¯å‘生在之å‰åœ¨åŒä¸€æ–‡æ¡£ä¸å®šä¹‰ä¹‹å‰ã€‚
在上é¢çš„例åä¸ï¼ŒB1å’ŒA2是冲çªå’ŒéžåŽŸåæ“作,并且都ä¸ä¼šå‘生在å¦ä¸€ä¸ªä¹‹å‰ã€‚è¿™å¾ˆæ˜Žæ˜¾ï¼Œå› ä¸ºæˆ‘åœ¨ä¸Šä¸€èŠ‚ä¸å·²ç»è¡¨æ˜Žï¼Œä¸¤è€…都å¯ä»¥åŒæ—¶å‘生。
这是C ++ 11ä¸å”¯ä¸€é‡è¦çš„事情。相比之下,如果å˜åœ¨æ•°æ®ç«žäº‰ï¼ŒJava内å˜æ¨¡åž‹ä¹Ÿä¼šå°è¯•å®šä¹‰è¡Œä¸ºï¼Œå¹¶ä¸”花了将近å年的时间æ‰èƒ½å¾—出åˆç†çš„规范。 C ++ 11没有犯åŒæ ·çš„错误。
我对这些基础知识并ä¸ä¸ºäººæ‰€çŸ¥æ„Ÿåˆ°æœ‰äº›æƒŠè®¶ã€‚确切的信æ¯æ¥æºæ˜¯C ++ 11æ ‡å‡†ä¸çš„多线程执行和数æ®ç«žäº‰éƒ¨åˆ†ã€‚但是,规范很难ç†è§£ã€‚
一个很好的起点是Hans Boehmçš„è°ˆè¯ - 例如å¯åœ¨çº¿è§‚看视频:
还有很多其他好的资æºï¼Œæˆ‘在其他地方已ç»æ到过,例如:
ç”案 1 :(得分:3)
没有对相åŒæ•°æ®çš„å¹¶è¡Œè®¿é—®ï¼Œå› æ¤æ²¡æœ‰é—®é¢˜ï¼š
Obj::Obj()
。Obj::Obj()
。obj
å 用的内å˜çš„å¼•ç”¨ä¼ é€’ç»™çº¿ç¨‹2。obj
å 用的内å˜çš„引用。唯一å¯èƒ½çš„问题是,如果Send
没有作为记忆障ç¢ï¼Œé‚£ä¹ˆå®ƒå°±ä¸ä¼šçœŸæ£æˆä¸ºâ€œåˆæ³•è½¬ç§»è®¾å¤‡â€ã€‚
ç”案 2 :(得分:2)
æ£å¦‚其他人所æåˆ°çš„ï¼Œæž„é€ å‡½æ•°ä¸æ˜¯çº¿ç¨‹å®‰å…¨çš„å”¯ä¸€æ–¹æ³•æ˜¯åœ¨æž„é€ å‡½æ•°å®Œæˆä¹‹å‰æŸç§æ–¹å¼ä»¥æŸç§æ–¹å¼èŽ·å–æŒ‡é’ˆæˆ–å¼•ç”¨å®ƒï¼Œå¹¶ä¸”å”¯ä¸€çš„æ–¹æ³•æ˜¯æž„é€ å‡½æ•°æœ¬èº«å…·æœ‰å°†this
指针注册到跨线程共享的æŸç§ç±»åž‹å®¹å™¨çš„代ç 。
现在,在您的具体示例ä¸ï¼ŒBranko Dimitrijevic完整地解释了您的案例是å¦æ£å¸¸ã€‚ä½†æ˜¯åœ¨ä¸€èˆ¬æƒ…å†µä¸‹ï¼Œæˆ‘ä¼šè¯´åœ¨æž„é€ å‡½æ•°å®Œæˆä¹‹å‰ä¸è¦ä½¿ç”¨æŸäº›ä¸œè¥¿ï¼Œå°½ç®¡æˆ‘è®¤ä¸ºåœ¨æž„é€ å‡½æ•°å®Œæˆä¹‹å‰æ²¡æœ‰ä»»ä½•â€œç‰¹æ®Šâ€ã€‚当它进入继承链ä¸çš„(最åŽä¸€ä¸ªï¼‰æž„é€ å‡½æ•°æ—¶ï¼Œè¯¥å¯¹è±¡å‡ ä¹Žå®Œå…¨â€œå¥½â€ï¼Œå…¶æ‰€æœ‰æˆå‘˜å˜é‡éƒ½è¢«åˆå§‹åŒ–,ç‰ç‰ã€‚所以没有比其他任何关键部分工作更糟糕,但是å¦ä¸€ä¸ªçº¿ç¨‹æˆ‘需è¦é¦–先了解它,并且å‘ç”Ÿçš„å”¯ä¸€æ–¹æ³•æ˜¯ï¼Œå¦‚æžœä½ åœ¨æž„é€ å‡½æ•°æœ¬èº«ä¸ä»¥æŸç§æ–¹å¼å…±äº«this
。所以,åªæœ‰è¿™æ ·åšæ‰èƒ½æˆä¸ºâ€œæœ€åŽä¸€ä»¶äº‹â€ã€‚
ç”案 3 :(得分:1)
如果您编写了两个线程,并且知é“ç¬¬ä¸€ä¸ªçº¿ç¨‹åœ¨ç¬¬äºŒä¸ªçº¿ç¨‹æ²¡æœ‰è®¿é—®å®ƒæ—¶ï¼Œå®ƒæ˜¯å®‰å…¨çš„ï¼ˆæœ‰ç‚¹ï¼‰ã€‚ä¾‹å¦‚ï¼Œå¦‚æžœæž„é€ å®ƒçš„çº¿ç¨‹åœ¨ä¼ é€’å¼•ç”¨/指针åŽä»Žä¸è®¿é—®å®ƒï¼Œé‚£ä¹ˆä½ å°±å¯ä»¥äº†ã€‚å¦åˆ™å®ƒæ˜¯çº¿ç¨‹ä¸å®‰å…¨çš„。您å¯ä»¥é€šè¿‡ä½¿è®¿é—®æ•°æ®æˆå‘˜ï¼ˆè¯»å–或写入)的所有方法é”定内å˜æ¥æ›´æ”¹å®ƒã€‚
ç”案 4 :(得分:0)
直到现在æ‰è¯»åˆ°è¿™ä¸ªé—®é¢˜......ä»ç„¶ä¼šå‘表我的评论:
当您处于多线程环境ä¸æ—¶ï¼Œæœ‰ä¸€ç§å¯é 的方法æ¥æž„é€ å¯¹è±¡ï¼Œå³ä½¿ç”¨é™æ€å±€éƒ¨å˜é‡ï¼ˆstatic local variable-CppCoreGuidelines),
从上é¢çš„引用ä¸å¯ä»¥çœ‹å‡ºï¼šâ€œè¿™æ˜¯ä¸Žåˆå§‹åŒ–顺åºç›¸å…³çš„问题最有效的解决方案之一。在多线程环境ä¸ï¼Œé™æ€å¯¹è±¡çš„åˆå§‹åŒ–ä¸ä¼šå¼•å…¥ç«žäº‰æ¡ä»¶ï¼ˆé™¤éžæ‚¨ä¸å°å¿ƒè®¿é—®å…±äº«å¯¹è±¡ï¼‰æ¥è‡ªå…¶æž„é€ å‡½æ•°ï¼‰ã€‚â€œ
å¦è¯·æ³¨æ„,如果X的销æ¯æ¶‰åŠéœ€è¦åŒæ¥çš„æ“作,则å¯ä»¥åœ¨å †ä¸Šåˆ›å»ºå¯¹è±¡å¹¶åŒæ¥ä½•æ—¶è°ƒç”¨æžæž„函数。
下é¢æ˜¯æˆ‘写的一个例å,显示Construct On First Use Idiom,这基本上就是å‚考文献所说的。
#include <iostream>
#include <thread>
#include <vector>
class ThreadConstruct
{
public:
ThreadConstruct(int a, float b) : _a{a}, _b{b}
{
std::cout << "ThreadConstruct construct start" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "ThreadConstruct construct end" << std::endl;
}
void get()
{
std::cout << _a << " " << _b << std::endl;
}
private:
int _a;
float _b;
};
struct Factory
{
template<class T, typename ...ARGS>
static T& get(ARGS... args)
{
//thread safe object instantiation
static T instance(std::forward<ARGS>(args)...);
return instance;
}
};
//thread pool
class Threads
{
public:
Threads()
{
for (size_t num_threads = 0; num_threads < 5; ++num_threads) {
thread_pool.emplace_back(&Threads::run, this);
}
}
void run()
{
//thread safe constructor call
ThreadConstruct& thread_construct = Factory::get<ThreadConstruct>(5, 10.1);
thread_construct.get();
}
~Threads()
{
for(auto& x : thread_pool) {
if(x.joinable()) {
x.join();
}
}
}
private:
std::vector<std::thread> thread_pool;
};
int main()
{
Threads thread;
return 0;
}
输出:
ThreadConstruct construct start
ThreadConstruct construct end
5 10.1
5 10.1
5 10.1
5 10.1
5 10.1