我正在查看遗留代码并发现以下片段:

MyClass::~MyClass()
{
   EnterCriticalSection(&cs);

//Access Data Members, **NO Global** members are being accessed here


  LeaveCriticalSection(&cs);
}

我想知道它是否有助于保护析构函数?

考虑一个场景:

1. Thread1 - About to execute any of the member function which uses critical section
2. Thread2-  About to execute destructor.

如果执行顺序是 1=>2 那么它可能会起作用。但如果顺序颠倒了怎么办?

是设计问题吗?

有帮助吗?

解决方案

析构函数应的当对象正在使用中不能被称为即可。如果你正在处理这样的情况,它需要一个根本性的修正。然而,析构函数也需要改变一些其他东西(这是无关的类被破坏),它可能需要一个关键部分(例如像递减的全球的计数器)。

其他提示

我认为你有一个更根本的问题。它不应该是法律摧毁你的对象上的一个线程,而另一个线程仍然调用成员函数。这本身就是错误的。

即使你成功的后卫关键部分的析构函数,当其他线程开始执行函数的其余部分会发生什么?将其(取决于它的配置场所)将垃圾内存或简单的一个无效的对象已删除的对象上这样做。

您需要更改代码,以确保该对象未在使用还是破坏时。

如果您正在访问全局变量,你可能需要线程安全的,是

例如。我的“窗口”类增加本身在构造函数列表“knownWindows”,并在析构函数删除自身。 “knownWindows”需要是线程,以便它们都锁定互斥,而他们这样做。

在另一方面,如果你的析构函数只访问被破坏对象的成员,你有一个设计问题。

定义 “线程安全”。这些都可能是现代计算的两个最一知半解的话。

但是,如果有来自两个不同的线程被输入两次析构函数的可能性(由于使用symchronisation对象意味着)你的代码是在深斗斗。被删除您询问应管理此对象的对象 - 这是(可能)在该级别的同步应该发生。

我已经看到了ACE线程,其中线程的ACE_Task_Base对象上运行,并且对象被从另一个线程被破坏的情况。析构函数获取一个锁,并通知所包含的线程,只是等待的条件之前。即在ACE_Task_Base信号运行的线程在出口处信号的状态,并允许析构完整和第一线程退出:

class PeriodicThread : public ACE_Task_Base
{
public:
   PeriodicThread() : exit_( false ), mutex_()
   {
   }
   ~PeriodicThread()
   {
      mutex_.acquire();
      exit_ = true;
      mutex_.release();
      wait(); // wait for running thread to exit
   }
   int svc()
   {
      mutex_.acquire();
      while ( !exit_ ) { 
         mutex_.release();
         // perform periodic operation
         mutex_.acquire();
      }
      mutex_.release();
   }
private:
   bool exit_;
   ACE_Thread_Mutex mutex_;
};

在此代码,析构函数必须使用线程安全技术来保证该对象未在运行的 SVC()离开线程之前销毁。

不会有所作为。如果如你所说,调用的顺序是相反的,那么你正在调用析构对象的成员函数,而这将会失败。同步不能修复该逻辑错误(对于初学者来说,所述成员函数调用将试图获得一个已经破坏的锁定对象)。

我第二尼尔巴特沃斯评论。绝对,负责删除和访问MyClass的各实体,应具有在此一检查。

此同步将实际从被创建类型MyClass的目的的时刻开始。

你的评论说“没有全球 成员正在此处被访问”,所以我猜不会。只有创建对象的线程才应该销毁它,那么您将保护它免受其他哪个线程的影响呢?

我自己喜欢有序的创建和销毁,其中只有一个对象拥有另一个子对象,并且引用该子对象的任何其他对象都是树中更靠下的后代。如果这些子对象中的任何一个代表不同的线程,那么它们将确保在销毁继续沿树进行之前完成。

例子:

  • main()创建对象A
    • 对象 A 包含对象 B
      • 对象 B 包含对象 C
        • 对象C创建一个访问对象A和B的线程
        • 对象 C 的析构函数运行,等待其线程完成
      • 对象 B 的析构函数运行
    • 对象 A 的析构函数运行
  • main() 返回

对象 A 和 B 的析构函数根本不需要考虑线程,而对象 C 的析构函数只需要与它选择创建自己的线程实现一些通信机制(例如等待事件)。

如果您开始将对象的引用(指针)分发给任意线程而不跟踪这些线程的创建和销毁时间,那么您只会遇到麻烦,但如果您这样做,那么您应该使用引用计数,并且如果那么当析构函数被调用时就为时已晚了。如果仍然存在对对象的引用,那么任何人都不应该尝试调用其析构函数。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top