2013-03-19 22 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