我正在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