在线程中等待我该怎么办?

时间:2009-12-14 18:59:17

标签: c++ visual-studio multithreading

我有一个主程序,它创建一个N子线程的集合来执行一些计算。从创建线程到完成任务的那一刻,每个孩子都将完全占据他们的任务。主程序还将创建一个特殊的(N + 1)线程,它有一些间歇性的任务要执行。当满足某些条件时(如全局变量具有某个值),特殊线程将执行计算,然后返回以等待再次满足这些条件。至关重要的是,当第N + 1个线程无关时,它不应该减慢其他处理器的速度。 有人可以建议如何实现这一点。

修改 明显但笨拙的方式是这样的:

// inside one of the standard worker child threads...
if (time_for_one_of_those_intermittent_calculations_to_be_done())
{
    global_flag_set = TRUE;
}

// inside the special (N+1)th thread
for(;;)
{
    if (global_flag_set == TRUE)
    {
        perform_big_calculation();
        global_flag_set = FALSE;
    }
    // sleep for a while?
}

7 个答案:

答案 0 :(得分:7)

您应该在Windows API中查看WaitForSingleObject和WaitForMultipleObjects函数。

WaitForMultipleObjects

答案 1 :(得分:2)

是。使用条件变量。如果您在条件变量上休眠,则线程将从runqueue中删除,直到已通知条件变量。

答案 2 :(得分:2)

您应该为此使用Windows同步事件,因此您的线程在等待时无所事事。有关详细信息,请参阅MSDN;我从CreateEvent()开始,然后转到hereOpenEvent()PulseEvent()和{{3}的其他与事件相关的函数SetEvent() }}

当然还有ResetEvent()WaitForSingleObject(),正如mrduclaw在下面的评论中指出的那样。

答案 3 :(得分:2)

WIN32的即用型条件类;)

class Condition {
private:
    HANDLE m_condition;
    Condition( const Condition& ) {} // non-copyable
public:
    Condition() {
        m_condition = CreateEvent( NULL, TRUE, FALSE, NULL );
    }
    ~Condition() {
        CloseHandle( m_condition );
    }
    void Wait() {
        WaitForSingleObject( m_condition, INFINITE );
        ResetEvent( m_condition );
    }
    bool Wait( uint32 ms ) {
        DWORD result = WaitForSingleObject( m_condition, (DWORD)ms );
        ResetEvent( m_condition );
        return result == WAIT_OBJECT_0;
    }
    void Signal() {
        SetEvent( m_condition );
    }
};

用法:

// inside one of the standard worker child threads...
if( time_for_one_of_those_intermittent_calculations_to_be_done() ) {
    global_flag_set = TRUE;
    condition.Signal();
}


// inside the special (N+1)th thread
for(;;) {
    if( global_flag_set==FALSE ) {
        condition.Wait(); // sends thread to sleep, until signalled
    }
    if (global_flag_set == TRUE) {
        perform_big_calculation();
        global_flag_set = FALSE;
    }
}

注意:您必须在global_flag_set周围添加锁定(例如关键部分)。而且在大多数情况下,标志应该用队列或至少一个计数器替换(线程可以在“特殊”线程执行其计算时多次发出信号)。

答案 4 :(得分:0)

缺少已经给出的更优选的选项,我通常只是在循环中产生CPU,直到满足所需的条件。

答案 5 :(得分:0)

基本上,你有N + 1个线程的两种可能性。

  • 如果它的工作很少,最好的办法就是让它睡觉,然后根据需要将它唤醒。罕见的上下文切换是微不足道的。
  • 如果它必须经常工作,那么你可能需要自动锁定它,即一个繁忙的等待状态,以防止它被重新安排或切换。

答案 6 :(得分:0)

每个全局变量都应该有一个N + 1线程的伴随事件。无论何时更改全局变量的状态,请将事件设置为信号状态。最好将这些变量隐藏在单例类私有属性中,并公开函数来获取和设置值。设置值的函数将进行比较,并在需要时设置事件。因此,您的N + 1线程将只是具有无限超时的WaitForMultipleObjects循环。应该使用另一个全局变量来表示应用程序作为一个整体退出,因此线程将能够退出。您可能只在最后一个线程完成后退出应用程序。因此,如果您需要提前退出,则必须通知所有线程他们必须退出。那些永久运行的线程可以通过定期读取变量来通知。那些正在等待的人,比如N + 1线程,应该通过事件通知。

人们建议使用CreateEvent(创建自动重置事件),SetEvent和WaitForMultipleObjects。我同意他们。 除了上述功能之外,其他人建议使用ResetEvent和PulseEvent。我不同意他们的看法。您不需要具有自动重置事件的ResetEvent。这个函数应该与手动复位事件一起使用,但手动复位事件的应用非常有限,您将在下面看到。

要创建自动重置事件,请调用CreateManvent Win32 API函数,并将bManualReset参数设置为FALSE(如果为TRUE,则该函数创建一个手动重置事件对象,需要使用ResetEvent函数来设置事件状态为无信号 - 这不是您所需要的)。如果此参数为FALSE,则该函数会创建一个自动重置事件对象,并且系统会在释放单个等待线程后自动将事件状态重置为非信号状态,即已从WaitForMultipleObjects或WaitForSigleObject等函数退出 - 但是,as as我之前写过,只会通知一个线程,而不是全部,所以你需要为每个等待的线程提供一个事件。由于您将只有一个等待的线程,因此您只需要一个事件。

关于PulseEvent - 它是不可靠的,不应该使用 - 请参阅https://msdn.microsoft.com/en-us/library/windows/desktop/ms684914(v=vs.85).aspx 只有那些线程被PulseEvent通知在"等待"现在调用PulseEvent的状态。如果它们处于任何其他状态,则不会通知它们,并且您可能永远不会确定线程状态是什么。等待同步对象的线程可以通过内核模式异步过程调用暂时从等待状态中删除,然后在APC完成后返回到等待状态。如果在线程从等待状态中删除期间发生对PulseEvent的调用,则不会释放该线程,因为PulseEvent仅释放那些在被调用时正在等待的线程。您可以在以下链接中找到有关内核模式异步过程调用(APC)的更多信息:   - https://msdn.microsoft.com/en-us/library/windows/desktop/ms681951(v=vs.85).aspx   - http://www.drdobbs.com/inside-nts-asynchronous-procedure-call/184416590   - http://www.osronline.com/article.cfm?id=75 您可以从以下文章中获得有关自动重置事件和手动重置事件的更多信息:   - https://www.codeproject.com/Articles/39040/Auto-and-Manual-Reset-Events-Revisited

关于手动重置事件,它们也可以在某些条件下使用,在某些情况下也可以使用。当您需要通知仅发生一次的全局状态更改的多个实例(例如应用程序退出)时,可以可靠地使用它们。

你只有一个等待的线程,但也许将来你会有更多的等待线程,所以这些信息将是有用的。

自动重置事件只能用于通知一个线程(如果有更多线程同时等待自动重置事件并且你设置了事件,只有一个线程将退出并重置它,其他线程的行为将会未定义)。从Microsoft文档中,我们可以假设只有一个线程会退出而其他线程不会退出,这不是很清楚。但是,我们必须考虑以下引用:“不要假设先进先出(FIFO)顺序。内核模式APC等外部事件可以更改等待顺序“Source - https://msdn.microsoft.com/en-us/library/windows/desktop/ms682655(v=vs.85).aspx

因此,当您需要非常快速地通知所有线程时 - 只需将手动重置事件设置为信号状态(通过调用SetEvent),而不是发出每个线程的每个自动重置事件的信号。一旦发出手动重置事件的信号,就不要再调用ResetEvent了。这个解决方案的缺点是线程需要在WaitForMultipleObjects数组中传递一个额外的事件句柄。数组大小是有限的,尽管MAXIMUM_WAIT_OBJECTS为64,实际上我们从未达到接近此限制。

乍一看,Microsoft文档似乎充满了行话,但随着时间的推移,您会发​​现它非常简单友好。无论如何,正确的多线程工作不是一个容易的主题,所以你必须容忍一定数量的行话