强制线程在销毁前离开对象

Force threads to leave object before destruction

在使用多线程时我经常运行遇到以下问题:

我有一个对象,比如网络接收器(但可以是任何东西)。以及获取数据的函数。现在有时根本就没有数据,你想让线程等待获取它的数据。阻塞调用,非常类似于 Berkeley 套接字及其派生实现。

原理很简单:

好吧,现在当然还有其他实现方法。但我通常使用 C++11 实现如下:

现在我的实际问题出现在 object B 的销毁上,如果它必须在 object A 之前销毁(返回 nullptr,或阻塞调用中的类似内容)。我真的不知道如何有效地结束 object B

主要问题是object B不知道线程,一个线程只能有一个句柄,在object A.

里面

示例:

一些代码来说明我的问题。

假设我在对象 B 中有这个函数:

data* getData
{
    std::unique_lock<std::mutex>l_newDataWaiterLock(m_newDataWaiterMutex);
    m_newDataWaiter.wait(l_newDataMutex);
    if(!running)
       return nullptr
    else 
       return data;
}

和这个析构函数:

~ObjectB()
{
    m_running = false;
    m_newDataWaiter.notifyAll();
    //Point X
}

使用这些成员变量:

std::condition_variable m_newDataWaiter;
std::atomic<bool> m_running;

我们仍然有问题,析构函数必须在指示的 Point X 处等待,直到所有其他线程都收到通知并返回 null。

现在我可以使用原子计数器、更多 std::condition_variables 和互斥锁来做点什么了。但我觉得必须有一个更优雅、更可靠的解决方案来解决这个问题:)。由于此解决方案需要在 object B.

的整个生命周期的每个 getData 调用中进行通知

注意:我使用的是 C++11,所以我用它来说明一切。我希望用它来解决它。虽然这当然是一个更普遍的并发问题。

如何使用 std::shared_ptr and using std::weak_ptr 管理对象 B 以将指针存储在 A 中?

效率稍微低一些,因为每次 A 想要获得访问权限时,它暂时需要通过 std::weak_ptr::lock() 自己获得一个 std::shared_ptr,但是你可以确定有不再存在竞争条件。

my actual problem arises on the destruction of object B, if it has to be destructed before object A

在这里,我描述了另一种处理对象实例删除的方法,该对象实例中可能包含线程 运行。嵌入式系统的行为可能与您的设计有很大不同......但也许它可以激发一种看待挑战的新方式。


总结:使用垃圾桶和看门人。

何时 'remove' 一个可能有其他线程在其中工作的对象

 1) copy pointer-to-object   to the 'waste-basket' fifo
    A* a;   waste_basket.push_back(a);  

 2) copy pointer-to-replacement-object to replace pointer-to-object
    A* t = new(replacementObject);
    a = t;  

 Use low priority janitor thread to inspect waste-basket periodically
 and delete any resident older than max duration. Max duratin ensures 
 any other thread activity has completed.

垃圾桶中的n秒,防止对象被删除 而线程仍在使用它。 (您的持续时间可能会有所不同。)


在我工作的嵌入式系统中,有 28 个被动(没有 内部线程)多态 table 中的对象实例(卡在 架子),以及大约 10 多个线程,它们可能在任何给定时间被 通过这些卡片实例之一与硬件交互。

即使操作员(在 ui 线程上)可能命令删除实例 j(书架中的第j张牌),对象可以被删除 直到当前在其中执行的所有线程都完成其当前 activity.

部分原因是每种方法的简短性(< 25 毫秒),我们做了 不使用计数,也不使用信号量来确定实例的未访问 状态。相反,我们分两步完成了移除转换, 并推迟了卡的实际删除。

步骤 1) 将 table 条目架 [j] 复制到 'waste-basket' fifo 列表,

步骤 2) 将 table 条目架 [j] 替换为 'empty slot' 实例指针,所有 shelf-using-threads 都知道如何使用的东西 多态使用(就好像它只是另一张卡片)。

这两个步骤使合乎逻辑的 删除看起来瞬间 用户和线程。但是什么时候真正删除?

所有现有线程在 'finished' 其 activity 时都需要 uired 离开实例。但那会持续多久?

我们提出的派生要求ui说明(基于设计的其他方面)是对象实例中的任何线程 'working' 应在 < 25 毫秒。从务实的角度来看,大多数完成得更快,none 更长。

因此,为了这个嵌入式系统和便利性,团队决定垃圾桶 'hold' 持续时间 >= 1 秒。

请注意,虽然移除的卡片位于垃圾桶中,但没有 其他线程不能访问它,也不能在同一个对象中启动另一个 activity。其他线程在发现架子上有一张 'empty slot' 卡片时被误导了。如果尚未从内存中删除,则该卡逻辑上已消失。

为了完成这个设计,我们添加了一个低优先级的管理员任务来定期处理垃圾桶列表。看门人检查实例进入垃圾桶的时间,至少在队列中等待1秒后才删除任何卡片。