2011-10-10 110 views
6

我正在編寫一個應用程序,其中大部分工作由後臺線程完成(10 - 500個線程)。如何將暫停/恢復功能添加到應用程序?

我想添加暫停/恢復功能。

之前,你可以用Thread.Suspend和Thread.Resume來做到這一點。但是這些功能現在被認爲已經過時。

有什麼可以讓我在平等的情況下做同樣的事嗎?

我正在用c編寫軟件#

+0

如果您需要任何有用的迴應,您需要提供更多信息。這些線程在做什麼?他們是如何開始的?他們在什麼條件下被「暫停?」沒有更多的信息,回答這個問題是不可能的。 –

+2

一個進程中的500個線程是一個*巨大數量的線程,特別是如果其中一堆線程大部分時間處於睡眠狀態。我的建議是重新設計你的架構,以便不使用比機器中的處理器更多的線程。線程在C#中非常「重」;你應該創造完成工作所需的絕對最低限度。你在做什麼,你正在創造這麼多的線程? –

+0

這是一個網絡爬行應用程序。每個線程花費大部分時間等待來自某個Web服務器的響應,所有線程都在相同的地方啓動,並且應該在用戶按下「暫停」按鈕時暫停。該應用程序通常需要700m-2G的內存,除非有更好的方法來做到這一點,否則我可以接受。即使在500個線程下,應用程序通常也只有不到30%的單個CPU運行 –

回答

2

已經用C#編寫高性能的爬蟲,我可以與一些權威機構,明確管理數十或數百個線程是不是最好的方式去說。它可以做到(我做到了),但是它極端痛苦。

這就是說。 。 。

如果你的應用程序是用我的思維方式,那麼每個線程做這樣的事情:

while (!Shutdown) 
{ 
    // get next url to crawl from somewhere 
    // download the data from that url 
    // do something with the data 
} 

暫停下載的線程是很容易的。我會建議製作兩個ManualResetEvent實例:一個用於繼續,另一個用於關閉。這些是static使得所有履帶線程可以訪問它們:

static ManualResetEvent ShutdownEvent = new ManualResetEvent(false); 
static ManualResetEvent ContinueEvent = new ManualResetEvent(true); 

然後,每個線程在一個循環中使用WaitAny

WaitHandle[] handles = new WaitHandle[] { ShutdownEvent, ContinueEvent }; 
while (true) 
{ 
    int handle = WaitHandle.WaitAny(handles); // wait for one of the events 
    if (handle == -1 || handle >= handles.Length) 
    { 
     throw new ApplicationException(); 
    } 

    if (handles[handle] = ShutdownEvent) 
     break; // shutdown was signaled 

    if (handles[handle] == ContinueEvent) 
    { 
     // download the next page and do something with the data 
    } 
} 

注意,當我所定義的handles陣列,我指定ShutdownEvent第一。原因是如果多個項目被髮信號通知,WaitAny返回與發信號對象相對應的最低索引。如果數組以其他順序填充,那麼如果不先暫停,則無法關閉。

現在,如果您希望線程關閉,請致電ShutdownEvent.Set。如果您希望線程暫停,請致電ContinueEvent.Reset當您希望線程恢復時,請致電ContinueEvent.Set

在下載過程中暫停有點困難。這是可能的,但問題是,如果您暫停太久,服務器可能會超時。然後,您必須從頭開始重新下載,或者如果服務器和您的代碼支持它,請從您停止的位置重新開始下載。這兩個選項都很痛苦,所以我不會建議在下載過程中暫停。

2

你的應用做什麼?

500個線程太多 - 這是1/2 GB的承諾內存僅用於堆棧。然後你有所有的上下文切換。

好,你想擺脫SuspendResume調用,但我建議你先看看你的架構 - 你可以移動到APM方法(BeginXXX/EndXXX)?

+0

這是一個網絡應用程序,它從網上下載信息。因爲它大部分時間都在等待響應,多線程對我來說似乎是個好主意。如果你知道更好的方法來完成它,請告訴我 –

2

作爲一個告誡,我要說什麼:不清楚你的應用程序做什麼;有許多使用線程池線程的簡單方法,例如TPL,後臺工作者等。

但是,如果您有創建的線程(而不是線程池)並且希望它們進行通信,則使用Monitor.Wait和Monitor。具有布爾阻塞條件的脈衝。

e.g:

bool _isPaused; 

void DoWork() 
{ 
     while (true) 
     { 
      lock (_locker) 
      { 
       while (_isPaused) Monitor.Wait(_locker); 

       // your worker code here 

      } 

     } 
} 
     // 
void UnPause() 
{ 
     lock (_locker) 
     { 
      _isPaused=false; 
      Monitor.PulseAll(_locker); 
     } 
} 
1

不是真的。暫停/恢復真的很簡單,直到他們崩潰你的應用程序,例如。通過暫停內存管理器或文件系統被鎖定的線程:(

通常的,稍微複雜的方法是在你的線程中找到你可以等待的地方,在你的情況下,我猜測最的線程通常在某些IO調用上被阻塞,因此被「掛起」,因此在讀取後執行「暫停」的好地方是爲了捕獲讀取返回的線程。可以通過檢查@Andrey建議的全局布爾'isRunning'標誌來實現實際掛起,如果需要掛起,則阻止全局ManualResetEvent掛起,清除事件,然後清除標誌。然後事件

如果使用全局變量會讓你感到噁心,那麼你可以傳入一個包含標誌,事件和'suspend(),'resume()'和'checkForSuspend()'方法的類的公共實例。

RGDS, 馬丁