【问题标题】:Force threads to leave object before destruction强制线程在销毁之前离开对象
【发布时间】:2015-07-26 06:52:13
【问题描述】:

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

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

原理很简单:

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

  • Object A 在专用于此任务的单独线程上调用对象 B 中的函数。
  • Object B 使用std::condition_variable 构造来阻塞线程,直到实际获取数据。
  • Object A 将数据放入队列,由主线程读取。

现在我的实际问题出现在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,所以我用它来说明一切。我希望用它来解决它。虽然这当然是一个更普遍的并发问题。

【问题讨论】:

    标签: c++ multithreading c++11 destructor blocking


    【解决方案1】:

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

    这样效率会稍微低一些,因为每次A 想要获得访问权限时,它都暂时需要通过std::weak_ptr::lock() 本身获得std::shared_ptr,但您可以确定不再存在竞争条件。

    【讨论】:

    • 啊,是的,这将是一个解决方案。但是,这也会阻止析构函数被调用吗?因此需要先前调用Object B 指示即将被破坏。还是我弄错了?
    • @laurisvr 这就是std::weak_ptr 的美妙之处:它不会阻止被引用的对象被破坏。只有当您(暂时)有一个std::shared_ptr 将数据放入其中时,才能防止破坏。只需使用if( const auto sp = wp.lock() ) { sp-&gt;insert(data); } 检查结果即可。不看整个代码就很难判断整体的生命周期管理,但试试看吧!
    【解决方案2】:

    我的实际问题出现在对象 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张卡片),该对象不能被删除 直到当前在其中执行的所有线程都完成了它们当前的活动。

    部分由于每种方法的简短性(

    步骤 1) 将表条目架子 [j] 复制到“废纸篓”fifo 列表,

    步骤 2) 将表条目架子[j] 替换为“空槽” 实例指针,所有货架使用线程都知道如何 多态使用(就好像它只是另一张卡片一样)。

    这两个步骤使逻辑上的删除显得瞬间 用户和线程。但删除实际何时发生?

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

    我们提出的派生要求(基于设计的其他方面)是对象实例内“工作”的任何线程都应在

    因此,为了这个嵌入式系统和便利性,团队决定废纸篓“保持”持续时间 >= 1 秒。

    请注意,虽然移除的卡片留在废纸篓中,但没有 其他线程可以访问它,也不能在同一个对象中启动另一个活动。当他们在架子上发现一张“空槽”卡时,其他线程被误导了。如果还没有从内存中删除,那张卡在逻辑上已经消失了。

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

    【讨论】:

      猜你喜欢
      • 2019-11-10
      • 2013-11-28
      • 2018-03-31
      • 2014-09-24
      • 1970-01-01
      • 2022-12-16
      • 2014-10-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多