C++ 通过事件的线程同步开销

C++ 通过事件的线程同步开销,c++,multithreading,winapi,events,thread-synchronization,C++,Multithreading,Winapi,Events,Thread Synchronization,我目前正在试验多线程同步。对于回溯,我有一组大约100000个对象——可能更多——我想以不同的方式每秒处理多次 现在我最关心的是同步的性能 这是我认为应该可以正常工作的(我省略了所有的安全方面,因为这只是一个测试程序,如果出现错误,程序将崩溃…)。我编写了两个函数,第一个由程序的主线程执行,第二个由所有附加线程运行 void SharedWorker::Start() { while (bRunning) { // Send the command to star

我目前正在试验多线程同步。对于回溯,我有一组大约100000个对象——可能更多——我想以不同的方式每秒处理多次

现在我最关心的是同步的性能

这是我认为应该可以正常工作的(我省略了所有的安全方面,因为这只是一个测试程序,如果出现错误,程序将崩溃…)。我编写了两个函数,第一个由程序的主线程执行,第二个由所有附加线程运行

void SharedWorker::Start()
{
    while (bRunning)
    {
        // Send the command to start task1
        SetEvent(hTask1Event);

        // Do task1 (on a subset of all objects) here

        // Wait for all workers to finish task1
        WaitForMultipleObjects(<NumberOfWorkers>, <ListOfTask1WorkerEvents>, TRUE, INFINITE);

        // Reset the command for task1
        ResetEvent(hTask1Event);

        // Send the command to start task2
        SetEvent(hTask2Event);

        // Do task2 (on a subset of all objects) here

        // Wait for all workers to finish task2
        WaitForMultipleObjects(<NumberOfWorkers>, <ListOfTask2WorkerEvents>, TRUE, INFINITE);

        // Reset the command for task2
        ResetEvent(hTask2Event);

        // Send the command to do cleanup
        SetEvent(hCleanupEvent);

        // Do some (on a subset of all objects) cleanup

        // Wait for all workers to finish cleanup
        WaitForMultipleObjects(<NumberOfWorkers>, <ListOfCleanupWorkerEvents>, TRUE, INFINITE);

        // Reset the command for cleanup
        ResetEvent(hCleanupEvent);
    }
}

DWORD WINAPI WorkerThreads(LPVOID lpParameter)
{
    while (bRunning)
    {
        WaitForSingleObject(hTask1Event, INFINITE);

        // Unset finished cleanup
        ResetEvent(hCleanedUp);

        // Do task1 (on a subset of all objects) here

        // Signal finished task1
        SetEvent(hTask1);

        WaitForSingleObject(hTask2Event, INFINITE);

        // Reset task1 event
        ResetEvent(hTask1);

        // Do task2 (on a subset of all objects) here

        // Signal finished task2
        SetEvent(hTask2);

        WaitForSingleObject(hCleanupEvent, INFINITE);

        // Reset update event
        ResetEvent(hTask2);

        // Do cleanup (on a subset of all objects) here

        // Signal finished cleanup
        SetEvent(hCleanedUp);
    }

    return 0;
}
void SharedWorker::Start()
{
当(布伦宁)
{
//发送命令以启动task1
SetEvent(hTask1Event);
//在此处执行task1(在所有对象的子集上)
//等待所有工人完成任务1
WaitForMultipleObjects(,真,无限);
//重置task1的命令
重置事件(hTask1Event);
//发送命令以启动task2
SetEvent(hTask2Event);
//在此处执行task2(在所有对象的子集上)
//等待所有工人完成任务2
WaitForMultipleObjects(,真,无限);
//重置task2的命令
重置事件(hTask2Event);
//发送命令进行清理
SetEvent(hCleanupEvent);
//执行一些(对所有对象的子集)清理
//等待所有工人完成清理工作
WaitForMultipleObjects(,真,无限);
//重置清除命令
重置事件(hCleanupEvent);
}
}
DWORD WINAPI WorkerThreads(LPVOID lpParameter)
{
当(布伦宁)
{
WaitForSingleObject(hTask1Event,无限);
//取消设置已完成的清理
重置事件(hCleanedUp);
//在此处执行task1(在所有对象的子集上)
//信号完成任务1
SetEvent(hTask1);
WaitForSingleObject(hTask2Event,无限);
//重置task1事件
重置事件(hTask1);
//在此处执行task2(在所有对象的子集上)
//信号完成任务2
SetEvent(hTask2);
WaitForSingleObject(hCleanupEvent,无限);
//重置更新事件
重置事件(hTask2);
//在此处执行清理(在所有对象的子集上)
//信号完成清理
SetEvent(hCleanedUp);
}
返回0;
}
为了指出我的要求,我只举一个小例子: 假设我们从上面得到100000个对象,分成8个子集,每个子集包含12500个对象,这是一个具有8个逻辑核的现代多核处理器。相关部分是时间。所有任务必须在大约8毫秒内完成


我现在的问题是,我是否可以从拆分处理中获得时间上的显著提升,或者通过事件进行同步是否太昂贵?或者,如果所有任务都需要以这种方式完成,是否还有其他方法可以以较少的工作量或处理时间同步线程?

如果您对单个对象的处理速度很快,请不要在线程之间拆分它。windows上的线程同步在每个上下文切换上都会消耗超过50毫秒的时间。这个时间不是由系统使用的,而是在系统上运行其他东西的时间

但是,如果每个对象处理大约需要8毫秒,则需要跨线程池安排工作。然而,对象处理可能会有所不同,并且大量工作线程将在不同的时刻完成工作

更好的方法是组织一个同步对象队列,向其中添加要处理的对象,并从中提取这些对象。此外,由于单个对象的处理比线程的调度间隔低得多,所以最好将它们分批处理到线程中(如10-20)。您可以通过测试估计池中的最佳工作线程数和批处理的最佳大小

因此伪代码可以如下所示:

main_thread:
    init queue
    start workers

    set counter to 100000
    add 100000 objects to queue
    while (counter) wait();

worker_thread:
    while (!done)
        get up to 10 objects from queue
        process objects
        counter -= processed count
        if (counter == 0) notify done

在不了解任务及其资源需求(CPU、I/O)的情况下,回答这个问题是不可能的。通常,您应该尽量减少线程在等待状态下花费的时间。异步处理是线程间信令的一种替代方法,但这在任务执行中可能不可能实现。哦,是的,对不起,完全忘记了我的资源需求。任务1和2是纯CPU,清理仅用于从任务2中延迟删除对象。不幸的是,异步处理是不可替代的,因为task1、task2和cleanup是一个固定的顺序,必须保持顺序,并且任何任务都必须在下一个任务开始之前完成。听起来,您可能能够使用类似OpenMP的东西,而不是滚动自己的线程/信号。这非常适合并行运行类似的顺序任务。谢谢你的提示和OpenMP,allready把我的鼻子插进去了。您是否有使用OpenMP的经验?我从第一次性能测试中得到了一些意想不到的结果。使用一个循环进行测试,测量固定次数的迭代所花费的时间(没有工作,因为我想知道哪一个消耗的时间更少)。所以我把一百万次迭代作为目标。在调试模式下,OpenMP在100%CPU下以每秒16.4k迭代次数(ips)的速度运行,在发布模式下,OpenMP在100%CPU下以440k ips的速度运行。我的解决方案在两种模式下都以46k IP和33%CPU的速度运行。从我的观点来看,OpenMP的结果很差?没有经验,只是听起来你的模式比使用自定义任务调度更适合于此。请为你的说法提供一个理由,即在Windows上切换上下文需要50毫秒。线程池的用途与我需要的不同。我的循环不断运行,一遍又一遍地执行相同的三个任务,直到停止。集合基本上保持不变(每次迭代不到十个新的/删除的对象),因此同步队列的开销很大。此外,循环的一次迭代可能需要8毫秒,而不是单个对象的处理。@SteveTownsend我没有