是否存在使用信号量而不通过 shared_ptr 延长生命周期的危险用例?
Are there use cases in which it dangerous to use semaphores without extending lifetime through shared_ptr?
我目前在以下设置中使用 void futures 进行线程同步:线程 A 将线程 B 提交到消息线程队列以产生线程 A 所需的资源。线程 A 等待资源,但如果它花费的时间太长,然后将在没有资源提供的功能的情况下继续。
对于 futures,对线程 B 的调用如下所示(伪代码):
/* Thread B function signature: void ThreadB(std::promise<void> p); */
std::promise<void> promised_resource;
std::future<void> future_resource {promised_resource.get_future()};
SubmitToMessageThread([p = std::move(promised_resource)]() mutable { ThreadB(std::move(p)); }));
if (future_resource.wait_for(std::chrono::seconds(1)) == std::future_status_ready)
/* work with resource if thread B has called p.set_value() to indicate the resource is ready */;
else
/* make do without the resource */
消息线程持续时间比线程 A 长,但线程 B 移动 promise
,因此线程 B 可以完全控制承诺的生命周期,并且不应该'即使线程 B 在线程 A 完成后处于 运行,也不会在其销毁后访问 promise
。 promise
和 future
之间的拆分允许每个线程控制它所负责的对象的生命周期。
情况与信号量不同,因为两个线程之间只共享一个对象(semaphore
)。最初,我打算为线程 B 提供对 semaphore
的引用。当可以保证线程 B 不会比线程 A 长寿时,这会起作用。但在我的设置中,情况并非如此。这是否意味着除了 semaphore
之外,我还必须使用 shared_ptr
(或其他类似机制)?
是的,您负责管理 semaphore
的生命周期,并且当创建 semaphore
的线程可能被另一个使用 semaphore
的线程超过生命周期时必须特别小心。
天真的方法是通过引用传递 semaphore
。虽然如果您可以保证引用在线程 B 的生命周期内保持有效,那将是可行的,但在所描述的情况下情况并非如此。
/* Thread B function signature: void ThreadB(std::shared_ptr<std::binary_semaphore>& done); */
auto done {std::make_shared<std::binary_semphore>(0)};
SubmitToMessageThread([&done]() { ThreadB(done); }));
if (done.wait_for(std::chrono::seconds(1)))
/* work with resource if thread B has called done->release() to indicate the resource is ready*/
else
/* make do without the resource */
但是线程 B 没有这样的生命周期保证。线程 A 的 wait_for
可能会在线程 B 完成之前过期,并且线程 A 可能会继续完成,导致 semaphore
在线程 B 使用之前被破坏。我们必须管理生命周期,通常使用shared_ptr
。比如下面的:
/* Thread B function signature: void ThreadB(std::shared_ptr<std::binary_semaphore> done); */
auto done {std::make_shared<std::binary_semphore>(0)};
SubmitToMessageThread([done]() { ThreadB(done); }));
if (done.wait_for(std::chrono::seconds(1)))
/* work with resource if thread B has called done->release() to indicate the resource is ready*/
else
/* make do without the resource */
总而言之,信号量提供了另一个步兵机会,除非仔细管理生命周期。
我目前在以下设置中使用 void futures 进行线程同步:线程 A 将线程 B 提交到消息线程队列以产生线程 A 所需的资源。线程 A 等待资源,但如果它花费的时间太长,然后将在没有资源提供的功能的情况下继续。
对于 futures,对线程 B 的调用如下所示(伪代码):
/* Thread B function signature: void ThreadB(std::promise<void> p); */
std::promise<void> promised_resource;
std::future<void> future_resource {promised_resource.get_future()};
SubmitToMessageThread([p = std::move(promised_resource)]() mutable { ThreadB(std::move(p)); }));
if (future_resource.wait_for(std::chrono::seconds(1)) == std::future_status_ready)
/* work with resource if thread B has called p.set_value() to indicate the resource is ready */;
else
/* make do without the resource */
消息线程持续时间比线程 A 长,但线程 B 移动 promise
,因此线程 B 可以完全控制承诺的生命周期,并且不应该'即使线程 B 在线程 A 完成后处于 运行,也不会在其销毁后访问 promise
。 promise
和 future
之间的拆分允许每个线程控制它所负责的对象的生命周期。
情况与信号量不同,因为两个线程之间只共享一个对象(semaphore
)。最初,我打算为线程 B 提供对 semaphore
的引用。当可以保证线程 B 不会比线程 A 长寿时,这会起作用。但在我的设置中,情况并非如此。这是否意味着除了 semaphore
之外,我还必须使用 shared_ptr
(或其他类似机制)?
是的,您负责管理 semaphore
的生命周期,并且当创建 semaphore
的线程可能被另一个使用 semaphore
的线程超过生命周期时必须特别小心。
天真的方法是通过引用传递 semaphore
。虽然如果您可以保证引用在线程 B 的生命周期内保持有效,那将是可行的,但在所描述的情况下情况并非如此。
/* Thread B function signature: void ThreadB(std::shared_ptr<std::binary_semaphore>& done); */
auto done {std::make_shared<std::binary_semphore>(0)};
SubmitToMessageThread([&done]() { ThreadB(done); }));
if (done.wait_for(std::chrono::seconds(1)))
/* work with resource if thread B has called done->release() to indicate the resource is ready*/
else
/* make do without the resource */
但是线程 B 没有这样的生命周期保证。线程 A 的 wait_for
可能会在线程 B 完成之前过期,并且线程 A 可能会继续完成,导致 semaphore
在线程 B 使用之前被破坏。我们必须管理生命周期,通常使用shared_ptr
。比如下面的:
/* Thread B function signature: void ThreadB(std::shared_ptr<std::binary_semaphore> done); */
auto done {std::make_shared<std::binary_semphore>(0)};
SubmitToMessageThread([done]() { ThreadB(done); }));
if (done.wait_for(std::chrono::seconds(1)))
/* work with resource if thread B has called done->release() to indicate the resource is ready*/
else
/* make do without the resource */
总而言之,信号量提供了另一个步兵机会,除非仔细管理生命周期。