C++ 删除对象时清理引用该对象的线程(在C+;+;)

C++ 删除对象时清理引用该对象的线程(在C+;+;),c++,thread-safety,destructor,C++,Thread Safety,Destructor,我有一个对象(Client*Client),它启动多个线程来处理各种任务(例如处理传入数据)。线程的启动方式如下: // Start the thread that will process incoming messages and stuff them into the appropriate queues. mReceiveMessageThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)receiveRtpMessageFunct

我有一个对象(Client*Client),它启动多个线程来处理各种任务(例如处理传入数据)。线程的启动方式如下:

// Start the thread that will process incoming messages and stuff them into the appropriate queues.
mReceiveMessageThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)receiveRtpMessageFunction, this, 0, 0);
这些线程都有对初始对象的引用,如下所示:

  // Thread initialization function for receiving RTP messages from a newly connected client.
  static int WINAPI receiveRtpMessageFunction(LPVOID lpClient)
  {
     LOG_METHOD("receiveRtpMessageFunction");
     Client * client = (Client *)lpClient;
     while(client ->isConnected())
     {
        if(client ->receiveMessage() == ERROR)
        {
           Log::log("receiveRtpMessageFunction Failed to receive message");
        }
     }

     return SUCCESS;
  }
客户机对象会定期被删除(出于各种充分的原因)。但当这种情况发生时,仍有对(现在已删除)对象的引用的处理线程在尝试访问该对象上的成员函数时会抛出一种或另一种异常

所以我确信有一个标准的方法来处理这种情况,但我还没有找到一个干净的方法。我不想仅仅终止线程,因为这不允许清理资源。我不能在对象上设置属性,因为正是对象上的属性变得不可访问


关于处理此问题的最佳方法的想法?

您需要一些其他状态对象,线程可以检查这些对象,以验证“客户端”仍然有效


一个选项是将客户端引用封装在其他保持持久性的对象中,并从线程中提供对该对象的引用。

您可以在线程中为客户端使用带有代理对象的观察者模式。代理就像智能指针一样,将访问转发到真正的客户端。当您创建它们时,它们向客户机注册自己,以便客户机可以从其析构函数中使它们失效。一旦它们失效,它们就会停止转发并返回错误。

我会通过向对象引入引用计数来解决这个问题。工作线程将保存引用,对象的创建者也将保存引用。不使用
delete
,而是从引用计数中递减,删除最后一个引用的人就是真正调用
delete
的人

您可以使用现有的引用计数机制(
shared\u ptr
等),也可以使用Win32 API
InterlockedIncrement()
InterlockedIncrement()
或类似的机制(可能引用计数是从1开始的
易失性DWORD


唯一缺少的另一件事是,当主线程释放其引用时,它应该向工作线程发出信号,让其删除自己的引用。你可以这样做的一个方法是通过;您可以将工作线程的循环重写为对的调用,当某个事件发出信号时,您可以认为这意味着工作线程应该清理并删除引用。

这可以通过向线程传递一个(boost)弱指针来处理。

由于线程正在运行,您没有多少余地

shared\u ptr
+
weak\u ptr
的任何组合都不能拯救您。。。您可以在对象有效时调用该方法,然后命令销毁它(仅使用
shared\u ptr
will)

我唯一能想象的是先终止各种进程,然后销毁对象。通过这种方式,您可以确保每个进程优雅地终止,并在必要时清理自己的混乱(它可能需要对象来完成)

这意味着您不能立即删除对象,因为您必须首先与使用对象的人重新同步,并且同步部分需要一些事件处理(因为您基本上希望告诉线程停止,而不是无限期地等待它们)

我将同步部分留给您,有许多备选方案(事件、标志等),我们没有足够的数据


您可以从析构函数本身或通过重载各种
delete
操作来处理实际的清理工作,以适合您的操作为准。

主题外:如果您的例程像预期的那样返回
DWORD
,则不需要强制转换到
LPTHREAD\u START\u例程
DWORD WINAPI函数(LPVOID)
这不仅仅是关于转发(这很容易),还包括中断已经在运行的任务。简短回答:不。不要中断已经在运行的线程。详细回答:查找我的。它确实有助于避免取消对已删除对象的引用,这是关键问题。当然,让线程知道对象消失也是一个好主意,这样它们就可以自己清理,但是在使用弱PTR时,不需要额外的同步。我同意这是可行的,我提出了一个没有引用计数的替代方案。。。但是不知道它是否会更快。
boost::shared_ptr
可以代替原始指针,尽管您仍然希望事件处理尽快停止进程,因为它们没有任何作用:)是的,但这会使“客户端”保持活动状态,这可能不是期望的效果……感谢指针。这就是我最后做的。我添加了一个公共方法Client::tellThreadsToFinishAndWait()。这将设置各种标志,告诉线程完成正在执行的操作并退出。它使用WaitForMultipleObjects来确定线程何时全部退出,然后将控制权返回给负责对非活动客户端进行垃圾收集的例程。