2013-03-19 17 views
0

我正在尝试多线程同步。对于一个背景,我有一套约100000个对象 - 可能更多 - 我想以不同的方式每秒处理多次。通过事件线程同步的开销

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

这是我认为应该工作得很好(我省略了所有的安全方面,因为这只是一个测试程序,如果出现错误,程序将会崩溃..)。我写了两个funktions,第一个由程序的主线程执行,第二个由所有其他线程运行。

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; 
} 

要指出我的要求,我只是给你一个小例子: 说我们得到了上述10万点的对象,分成12500个对象中的每个,现代多核处理器的8子集有8个逻辑核心。相关部分是时间。所有任务必须在大约8ms内完成。

我现在的问题是,我能从分割处理中获得显着的提升,还是通过事件的同步过于昂贵?或者如果所有任务都需要这样完成,甚至有另一种方法可以用更少的努力或处理时间同步线程?

+1

这是不可能回答这个不知道更多关于你的任务,他们的资源需求(CPU,I/O)。一般来说,你应该尽量减少你的线程在等待状态下花费的时间。异步处理是线程间信号传递的一种替代方法,但这在您的任务执行中可能不可行。 – 2013-03-19 13:33:13

+0

哦,对不起,完全忘记了我的资源需求。任务1和2是纯粹的CPU,清理仅用于从任务2中延迟删除对象。不幸的是,异步处理是无法替代的任务1,任务2和清理是固定的序列,必须维持顺序,并且必须完成任务可能会开始。 – rootmenu 2013-03-19 14:22:53

+0

这听起来像你可能能够使用OpenMP之类的东西,而不是滚动你自己的线程/信号。这非常适合并行运行类似的顺序任务。 http://msdn.microsoft.com/en-us/library/tt15eb9t(v=vs.110).aspx – 2013-03-19 14:29:29

回答

0

如果您对单个对象的处理速度很快,请不要在线程之间进行拆分。 Windows上的线程同步在每个上下文切换时都会消耗50ms以上的时间。系统不使用这段时间,而只是在系统上运行其他内容的时间。

但是,如果每个对象处理大约需要8ms,那么跨线程池就有一个工作计划点。然而,对象处理可能会有所不同,并且大量工作者线程会在不同的时刻完成工作。

更好的方法是组织一个同步的对象队列,您可以添加要处理的对象,并从中处理它们。此外,由于单个对象的处理比线程的调度间隔低很多,所以最好将它们分批处理(如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 
+1

请提供一个理由为您的声明上下文切换在Windows上需要50ms。 – 2013-03-19 14:12:59

+0

线程池与我需要的目的不同。我的循环不断运行,一遍又一遍地执行相同的三个任务,直到停止。该集合保持大致相同(每次迭代少于10个新/删除的对象),因此同步队列将是一个很大的开销。此外,循环的一次迭代可能需要8ms,而不是单个对象的处理。 – rootmenu 2013-03-19 14:33:23

+0

@SteveTownsend我没有一个准确的数字,但是这里是一个链接:http://stackoverflow.com/questions/2898344/how-long-does-it-take-each-thread-timeslice-in-windows- XP。根据我的经验,线程可能会在上下文切换时超过50毫秒(如问题注释)。 – 2013-03-19 14:50:23