是否存在使用信号量而不延长shared_ptr生存期的用例?



我目前在以下设置中使用 void 期货进行线程同步:线程 A 将线程 B 提交到消息线程队列以生成线程 A 所需的资源。 线程 A 等待资源,但如果花费太长时间,则会放弃,然后在没有资源提供的功能的情况下继续。

对于期货,对线程 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 可以完全控制承诺的生存期,并且即使在线程 A 完成后线程 B 正在运行,也不应有在销毁后访问promise的风险。promisefuture之间的拆分允许每个线程控制其负责的对象的生存期。

信号量的情况不同,因为两个线程之间只有一个对象(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 可能会继续完成,从而导致线程 B 使用之前semaphore被破坏。我们必须管理生命周期,通常用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 */

总之,信号量提供了另一个脚枪机会,除非仔细管理生命周期。

最新更新