訪問地圖後有一個互斥鎖是不夠的。如果沒有互斥體,則無法到達地圖附近的任何地方,因爲另一個線程在讀取地圖時可能需要互斥體修改地圖。
{
std::unique_lock<std::mutex> lock(my_mutex);
std::map<std::thread::id, doSomething *>::iterator it = maps.find(std::this_thread::get_id());
if (it != maps.end())
return *it;
auto ptr = std::make_unique<doSomething>();
maps[std::this_thread::get_id()] = ptr.get();
return ptr.release();
}
但除非你有一些特殊/獨特的使用情況下,這是通過thread-local storage一個已經解決的問題,因爲你有C++ 11你有thread_local
storage specifier。
請注意,此處我使用的是mutex
,因爲cout
是共享資源,yield
只是爲了鼓勵更多的交錯工作流。
#include <iostream>
#include <memory>
#include <thread>
#include <mutex>
static std::mutex cout_mutex;
struct CoutGuard : public std::unique_lock<std::mutex> {
CoutGuard() : unique_lock(cout_mutex) {}
};
struct doSomething {
void fn() {
CoutGuard guard;
std::cout << std::this_thread::get_id() << " running doSomething "
<< (void*)this << "\n";
}
};
thread_local std::unique_ptr<doSomething> tls_dsptr; // DoSomethingPoinTeR
void testFn() {
doSomething* dsp = tls_dsptr.get();
if (dsp == nullptr) {
tls_dsptr = std::make_unique<doSomething>();
dsp = tls_dsptr.get();
CoutGuard guard;
std::cout << std::this_thread::get_id() << " allocated "
<< (void*)dsp << "\n";
} else {
CoutGuard guard;
std::cout << std::this_thread::get_id() << " re-use\n";
}
dsp->fn();
std::this_thread::yield();
}
void thread_fn() {
testFn();
testFn();
testFn();
}
int main() {
std::thread t1(thread_fn);
std::thread t2(thread_fn);
t2.join();
t1.join();
}
現場演示:http://coliru.stacked-crooked.com/a/3dec7efcb0018549
g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
140551597459200 allocated 0x7fd4a80008e0
140551597459200 running doSomething 0x7fd4a80008e0
140551605851904 allocated 0x7fd4b00008e0
140551605851904 running doSomething 0x7fd4b00008e0
140551605851904 re-use
140551605851904 running doSomething 0x7fd4b00008e0
140551597459200 re-use
140551605851904 re-use
140551597459200 running doSomething 0x7fd4a80008e0
140551605851904 running doSomething 0x7fd4b00008e0
140551597459200 re-use
140551597459200 running doSomething 0x7fd4a80008e0
這一點很難被發現,但線程 '9200分配..4a80 ..而線程' 1904分配..4b00 ..
這是一個如果C++ 11由於thread_local支持而可用,那麼這個答案很好。如果C++ 11不可用,則線程創建者通過線程參數(指向包含上下文的對象的指針)向其提供所有必需的線程上下文是一種好方法。 – qexyn
大多數編譯器在十多年前都有一些線程本地存儲的變體。 https://en.wikipedia.org/wiki/Thread-local_storage#C_and_C.2B.2B https://msdn.microsoft.com/en-us/library/9w1sdazb(v=vs.71).aspx – kfsone
這就是沒錯,但是沒有C++ 11,TLS就不那麼簡單了,尤其是在編寫跨平臺代碼的時候。這就是爲什麼我建議傳遞上下文對象,因爲與TLS相比,這是一種更簡單的方法,並且每個線程庫都支持將void *傳遞給新線程的概念。 – qexyn