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

C++ 在销毁之前强制线程离开对象,c++,multithreading,c++11,destructor,blocking,C++,Multithreading,C++11,Destructor,Blocking,使用多线程时,我经常遇到以下问题: 我有一个对象,比如说一个网络接收器(但可以是任何东西)。以及一个获取数据的函数。现在,有时根本没有数据,您希望让线程等待获取数据。阻塞调用,非常类似于Berkeley套接字及其派生实现所使用的调用 原则很简单: 当然,现在还有其他方法来实现这一点。但我通常使用C++11实现如下: 对象A在单独的线程上调用对象B中的函数,该线程专用于此任务 对象B使用std::condition\u变量构造来阻止线程,直到实际获取数据为止 对象A将数据放入队列中,该队列由主

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

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

原则很简单:

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

  • 对象A
    在单独的线程上调用对象B中的函数,该线程专用于此任务
  • 对象B
    使用
    std::condition\u变量
    构造来阻止线程,直到实际获取数据为止
  • 对象A
    将数据放入队列中,该队列由主线程读取
现在,我的实际问题出现在销毁
对象B
,如果它必须在
对象A
之前销毁(在阻塞调用中返回null ptr或类似内容)。我真的不知道如何有效地包装
对象B

主要问题是
对象B
不知道线程,一个线程只能有一个句柄,它位于
对象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;
}
使用这些成员变量:

std::condition_variable m_newDataWaiter;
std::atomic<bool> m_running;
std::条件变量m\u newdatawater;
std::原子m_运行;
我们仍然存在这样一个问题:析构函数必须在指定的
点X
处等待,直到所有其他线程都收到通知并返回null

现在我可以用原子计数器、更多的std::condition_变量和互斥量来制作一些东西。但我有一种感觉,对于这个问题,必须有一个更优雅、更可靠的解决方案:)。由于此解决方案需要在对象B的整个生命周期内的每次
getData
调用中都发出通知


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

如何使用a管理对象
B
,并使用a将指针存储在
a

这稍微有点低效,因为每当
A
想要获得访问权限时,它暂时需要通过自身获得
std::shared_ptr
,但您可以确保不再存在竞争条件

如果必须的话,我的实际问题出现在对象B的破坏上 在对象A之前被破坏

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


小结:使用废纸篓和看门人

何时“删除”可能有其他线程在其中工作的对象

 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个是被动的(没有 内部线程)多态表中的对象实例(中的卡) 在任何给定的时间,大约有10多个线程 通过这些卡实例之一与硬件交互

即使操作员(在ui线程上)可能会命令删除实例 j(货架上的第j张卡片),对象不能删除 直到当前在中执行的所有线程完成其当前活动

部分原因是由于每种方法的简单性质(<25 ms),我们没有这样做 不使用计数或信号量来确定实例的未访问状态 国家。相反,我们分两步完成了删除转换, 并推迟了卡的实际删除

步骤1)将表格输入架[j]复制到“废纸篓”fifo列表中

步骤2)用“空槽”替换表条目架[j] 实例指针,所有使用线程的工具架都知道如何 使用多态性(好像它只是另一张卡)

这两个步骤使逻辑删除看起来是即时的 用户和线程。但何时会真正发生删除

所有现有线程都需要在“完成”其活动时离开实例。但这可能需要多长时间

我们提出的派生要求(基于设计的其他方面)是,对象实例中的任何线程“工作”都应在<25毫秒内完成该活动。从实用角度讲,大多数线程的完成速度要快得多,没有一个线程的完成时间更长

因此,为了这种嵌入式系统和方便,团队决定废纸篓的“保持”时间应大于等于1秒

请注意,当取出的卡位于废纸篓中时,没有 其他线程既不能访问它,也不能在同一对象内启动另一个活动。其他线程在书架上发现一张“空插槽”卡时被错误引导。如果还没有从内存中删除的话,该卡在逻辑上就消失了


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

啊,是的,这将是一个解决方案。但是这也会阻止析构函数被调用,对吗?因此需要先前调用
对象B
,指示a即将被破坏。还是我弄错了?@laurisvr这就是
std::weak\u ptr
的美妙之处:它不会阻止被引用对象被破坏。只有在你(临时)的时候
 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.