我正在Windows下創建一個線程,它需要一個參數塊作爲void *(LPVOID)傳遞。通過需要void *參數的函數傳遞unique_ptr的最安全方法是什麼?
調用函數堆分配參數塊,因爲生成的線程可能超過調用函數。調用函數不需要共享參數塊的所有權。
這裏是我的想法至今:
...
using Functor = std::function<void()>
class SpawnData
{
public:
SpawnData(Functor func) : func(func) {}
Functor func;
};
DWORD WINAPI MyTaskLauncher(LPVOID ppRawData)
{
assert(ppRawData != nullptr);
//auto pData = *static_cast<std::unique_ptr<SpawnData>*>(ppRawData); //not permitted (A)
auto ppData = static_cast<std::unique_ptr<SpawnData>*>(ppRawData); //Feels safe, but...
assert(*ppRawData != nullptr);
(*ppRawData)->func(); //could dereference invalid memory?
}
ThreadId MyThreadSpawner(Functor func)
{
auto pData = std::make_unique<SpawnData>(func);
//CreateThread() is Windows API with a void* signature for the data param
... = CreateThread(..., static_cast<LPTHREAD_START_ROUTINE>(MyTaskLauncher),
static_cast<LPVOID>(&(pData.get())), ...); //Ugh... :(
threadId = ...;
return threadId;
}
我的問題:
1)你是否同意有開始調用的CreateThread(比賽條件)MyTaskLauncher重新包裝MyThreadSpawner()之前的原始指針退出?
2)你是否同意Q1)本質上是沒有實際意義,因爲MyThreadSpawner()的pData中會被破壞,當它超出範圍無論MyTaskLauncher已經‘包裝’它的內存與否?
3)通過CreateThread()API去除,傳遞和重新包裝我的智能指針的最安全方法是什麼?
考慮以下幾點:
DWORD WINAPI MyTaskLauncher(LPVOID pRawData)
{
assert(pRawData != null)
auto pData = std::make_unique<SpawnData>(*static_cast<SpawnData*>(pRawData));
//signal MyThreadSpawner() that SpawnData is safely wrapped
//...do work...
return result;
}
ThreadId MyThreadSpawner(Functor func)
{
auto pData = std::make_unique<SpawnData>(func);
//CreateThread() is Windows API with a void* signature for the data param
... = CreateThread(..., static_cast<LPTHREAD_START_ROUTINE>(MyTaskLauncher),
static_cast<LPVOID>(pData.get()), ...); //Hmm...
threadId = ...;
//Wait for MyTaskLauncher to signal SpawnData is safely wrapped
return threadId;
}
4)這是合法的嗎?一時間,會有兩個,呃,指向同樣的內存unique_ptrs ...
如果我更新此使用的shared_ptr如下:
//MyTaskLauncher:
...
auto pData = *static_cast<std::shared_ptr<SpawnData>*>(pRawData)); // (B)
...
//MyThreadSpawner:
auto pData = std::make_shared<SpawnData>(func);
...
5)的行標(B) ,以上是合法的,而(A)遠高於(與std::unique_ptr<SpawnData>*
相同)不是。任何人都可以闡明爲什麼?
6)最後,關於更簡單和/或更安全的技術,通過需要void *的函數簽名傳遞安全數據的任何建議。
在此先感謝您的想法。
沒有「安全」的方式來做到這一點,一旦你轉向'void *',類型系統關閉,&&pData'是堆棧中的一個地址。 – imreal
在我看來,調用者根本不應該使用'unique_ptr'。新的線程可能會使用'unique_ptr',但我認爲調用者最好使用原始指針。 – user2357112
@user這可能是務實的答案,我授予你,但隨着解決方案的老化和維護,我會睡得更好,知道堆分配儘可能得到了最大程度的保護。此外,我覺得像非類型安全的API依賴性這樣的障礙應該明確地不會造成放棄代碼中其他地方的最佳實踐的漣漪效應。 – U007D