2009-11-15 25 views
8

我如何可以等待分离线程在C++来完成?等待分离的线程来完成在C++

我不关心的退出状态,我只是想知道线程是否已经完成。

我试图提供围绕异步thirdarty工具同步封装。问题是一个涉及回调的奇怪的竞赛状况崩溃。的进程是:

  1. 我所说的第三方,并注册一个回调
  2. 当第三方完成,它使用回调通知我 - 在一个分离的线程我没有真正的控制权。
  3. 我想从(1)要等到(2)被称为线程。

我想提供一个阻塞调用的机制来包装这个。到目前为止,我有:

class Wait { 
    public: 
    void callback() { 
    pthread_mutex_lock(&m_mutex); 
    m_done = true; 
    pthread_cond_broadcast(&m_cond); 
    pthread_mutex_unlock(&m_mutex); 
    } 

    void wait() { 
    pthread_mutex_lock(&m_mutex); 
    while (!m_done) { 
     pthread_cond_wait(&m_cond, &m_mutex); 
    } 
    pthread_mutex_unlock(&m_mutex); 
    } 

    private: 
    pthread_mutex_t m_mutex; 
    pthread_cond_t m_cond; 
    bool   m_done; 
}; 

// elsewhere... 
Wait waiter; 
thirdparty_utility(&waiter); 
waiter.wait(); 

据我所知,这应该工作,它通常会,但有时它会崩溃。据我可以从核心文件确定,我猜测,这个问题是这样的:

  1. 当回调广播m_done结束,等待线程醒来
  2. 等待线程现在都是在这里完成,等待被破坏。 Wait的所有成员都被销毁,包括互斥和cond。
  3. 回调线程试图从广播点继续,但现在使用的内存的被释放,从而导致内存损坏。
  4. 当回调线程试图返回时(高于我可怜的回调方法的级别),程序崩溃(通常使用SIGSEGV,但我曾多次看到SIGILL)。

我试了很多不同的机制来尝试解决这个问题,但是他们都没有解决这个问题。我仍然偶尔看到碰撞。

编辑:更多详细信息:

这是一个大规模多线程应用程序的一部分,所以创建静态等待是不切实际的。

我跑了一个测试,在堆上创建Wait,并故意泄漏内存(即Wait对象从不释放),导致没有崩溃。所以我相信这是一个等待被释放太快的问题。

我也试过在wait解锁后用sleep(5)进行测试,也没有产生崩溃。尽管如此,我讨厌依赖于这样的混乱。

编辑:第三方细节:

我不认为这是在第一个相关的,但我认为它的越多,我认为这是真正的问题:

的第三方的东西我提到过,为什么我无法控制线程:这是使用CORBA。

因此,CORBA有可能比我预期的更长时间地坚持对我的对象的引用。

回答

3

是的,我相信你正在描述的是正在发生的事情(取消分配的竞争条件)。解决这个问题的一个快速方法是创建一个静态的Wait实例,一个不会被破坏的实例。只要你不需要同时有一个以上的服务员,这将会起作用。

您也将永久使用该内存,它不会释放。但它看起来不那么糟糕。

主要问题是很难在线程之间协调线程通信结构的生命周期:至少在没有垃圾回收的语言中,至少需要一个剩余的通信构造来进行通信C++)。

编辑: 请参阅有关使用全局互斥对refcounting的一些想法的注释。

+0

不幸的是,这是一个大规模的多线程应用程序,我们真的想为每个应用程序分别使用不同的Wait对象 - 否则它会让我们太慢。 – Tim 2009-11-15 03:18:45

+0

另外,如果我们使用一个静态的Wait,那么就有一个问题来试图协调哪个线程需要恢复。 – Tim 2009-11-15 03:28:13

+0

好的,你可以做到这一点。您可以将一个refcount字段添加到由全局互斥锁保护的Wait对象。开始2的refcount,然后有回调和服务员都完成后递减refcount。如果全局互斥变成瓶颈,还有其他更复杂的解决方案。 – 2009-11-15 03:29:25

0

据我所知,直接询问线程是否完成运行(即没有0​​函数),就没有可移植的方法。你在做什么正确的方式来做到这一点,至少在你有信号的情况下。如果你看到崩溃,你肯定是由于Wait对象正在被释放当创建它的线程退出(而不是一些其他微妙的锁定问题 - 这很常见),问题是,你需要确保Wait不是被解除分配,通过管理一个不同于通知的线程。将其放入全局内存或动态分配它并与该线程共享。最简单的就是没有为自己的内存等待等待,有线程正在等待拥有它。

0

您是否正确初始化并销毁互斥锁​​和条件变量?

Wait::Wait() 
{ 
    pthread_mutex_init(&m_mutex, NULL); 
    pthread_cond_init(&m_cond, NULL); 
    m_done = false; 
} 

Wait::~Wait() 
{ 
    assert(m_done); 
    pthread_mutex_destroy(&m_mutex); 
    pthread_cond_destroy(&m_cond); 
} 

确保您不会过早破坏Wait对象 - 如果它在一个线程中被摧毁,而另一个线程仍然需要它,你会得到一个竞争条件,这将有可能导致段错误。我建议使它成为一个全局静态变量,它在程序初始化时(在main()之前)构造并在程序退出时被破坏。

+0

是的,互斥和cond被正确初始化/销毁。我实际上使用了经过良好测试的包装类。是的,我确定Wait被过早销毁 - 而一个线程仍然在Wait :: callback中。 – Tim 2009-11-15 03:30:51

0

如果你的假设是正确的,那么第三方模块看起来是越野车,你需要想出一些破解来让你的应用程序工作。

Static Wait是不可行的。如何Wait池(它甚至可能按需增长)?你使用线程池运行应用程序吗? 尽管在第三方模块仍在使用时仍会重复使用相同的Wait。但是,您可以通过适当排列池中空置的等待来最大限度地减少此类机会。

声明:我绝不是线程安全方面的专家,因此请将此文作为外行的建议。