2012-02-19 56 views
0

我有一個應用程序,我有很多「搜索」在同一時間運行(在搜索需要1 - 10秒才能完成,這取決於有多少結果是可用的)問題是,搜索時的延遲越來越大(我認爲,因爲25最大線程)我使用Backgroundworker類Atm。於是我查閱了一些其他的實現:線程最好的實現

簡單的例子:

static void Main() 
{ 

    for (int i = 0; i < 500; i++) 
    { 
     try 
     { 
      new Thread(new ParameterizedThreadStart(doWork)).Start(i); 
     } 
     catch { } 
    } 
    Console.ReadLine(); 
} 
static void doWork(object i) 
{ 
    Console.WriteLine(i + ": started"); 
    Thread.Sleep(1000); 
    Console.WriteLine(i + " done"); 
    Thread.CurrentThread.Abort(); 
} 

,但我得到的是我中止線程中的異常(至極讓我擔心) 所以,我想蒙山線程池:

static void Main() 
{ 

    for (int i = 0; i < 500; i++) 
    { 
     ThreadPool.QueueUserWorkItem(new WaitCallback(doWork), i); 
    } 
    Console.ReadLine(); 
} 
static void doWork(object i) 
{ 
    Console.WriteLine(i + ": started"); 
    Thread.Sleep(1000); 
    Console.WriteLine(i + " done"); 
} 

但是這變得味道很慢...

我仍然在尋找最佳實施,任何人都可以幫助我嗎?

編輯:DoWork的方法建立網絡連接(並等待它完成)這是一個API,所以我不能做異步

+0

你爲什麼中止線程? 500線程永遠不是一個好主意 – 2012-02-19 11:44:00

+0

你爲什麼中止線程?一旦達到doWork的最後,線程也會死亡。 – 2012-02-19 11:45:59

+0

這些線程在做什麼?他們是CPU綁定,還是IO綁定?如果他們是IO綁定,請考慮使用非阻塞IO,而不是每個搜索都有一個線程。 – CodesInChaos 2012-02-19 11:56:54

回答

0

線程池嘗試最小化創建的線程數,而不是爲排隊的任務創建新線程,而是等待池中的其他線程被釋放。這是有原因的 - 太多的線程會妨礙性能。但是,您可以在發生此限制之前覆蓋要創建的最小線程數。

這裏是你的原代碼修正,使其工作速度快:

static void Main() 
{ 
    int minWorker, minIOC; 
    ThreadPool.GetMinThreads(out minWorker, out minIOC); 
    ThreadPool.SetMinThreads(50, minIOC); 
    for (int i = 0; i < 500; i++) 
    { 
     ThreadPool.QueueUserWorkItem(new WaitCallback(doWork), i); 
    } 
    Console.ReadLine(); 
} 

static void doWork(object i) 
{ 
    Console.WriteLine(i + ": started"); 
    Thread.Sleep(1000); 
    Console.WriteLine(i + " done"); 
} 
+0

強制ThreadPool啓動大量線程並不是解決性能問題的好方法。如果你要這樣做,爲什麼不簡單地手動啓動500個線程,而不是強制TP以一種無意的方式工作。這可能會對依賴於ThreadPool保持響應的衆多其他.NET特性產生可怕的後果。 -1 – spender 2012-02-19 17:34:59

+0

你真的讀過我的回覆嗎?我寫了太多線程會傷害性能。但SetMinThreads可用,因爲在創建新線程之間ThreadPool 0.5秒延遲是一個重大瓶頸的情況。在這裏尋找單詞「bursts」:http://msdn.microsoft.com/en-us/library/system.threading.threadpool.setminthreads%28v=VS.90%29.aspx – 2012-02-19 19:44:09

+0

另外,100個線程並不是很多 - ThreadPool中默認的最大線程數是250個內核數。有時候,從更高的數字開始,比如當你期望同時運行的許多請求在同時運行時表現良好時,這是有益的。 – 2012-02-19 19:59:41

0

不要使用Thread.Abort。這是一個相當暴力的結局,只是想要優雅地結束一個糟糕的工作威脅。您可以讓原始的doWork方法結束,並且線程將被釋放。

和關於你的第二個解決方案 - 你在第二,這是很多超過線程池可以同時運行排隊500個線程。它需要更多時間,因爲它們一個接一個地運行。

另一種選擇,在.NET 4.0中,在System.Threading.Tasks任務庫,這是一個聰明的基於線程池的解決方案可以考慮。

但要回到原來的問題 - 究竟是什麼,你的「延遲變長」使用BackgroundWorkers時,是什麼意思?你的意思是說,當有多個搜索進行時,每個搜索需要更長的時間(接近10秒)?如果是這樣,我會嘗試尋找其他地方的瓶頸。什麼是「搜索」?你在訪問數據庫嗎?網絡連接?也許你鎖定了導致瓶頸的應用程序部分。

+0

我的意思是後臺工作人員也會自動排隊請求,所以如果我每10秒鐘就會在100個事情中及時搜索,那麼排隊會變得很大,而且他們是一個延遲。 – Svexo 2012-02-19 12:43:13

0

1)如果你打電話中止,你會得到一個ThreadAbortException:Thread.Abort

2)¿500個線程? (除非你正在使用圖形處理器)

1

如果搜索是CPU綁定,我會使用Parallel.For或並行linq,手動指定MaxDegreeOfParallelism。在這種情況下,通常虛擬內核的數量是最佳線程數。

如果搜索上的東西外等待(開往例如IO,等待在網絡上的響應,...),我會考慮非阻塞的API,所以你不需要每次搜索的線程。

1

嘗試這種解決方案:

static void Main(string[] args) 
{ 
    for (int i = 0; i < 100; i++) 
    { 
     Task t = new Task(doWork, i); 
     t.Start(); 
    } 
    Console.ReadLine(); 
} 

static void doWork(object i) 
{ 
    Console.WriteLine(i + ": started"); 
    Thread.SpinWait(20000000); // It depends on what doWork actually does whether SpinWait or Sleep is the most appropriate test 
    //Thread.Sleep(1000); 
    Console.WriteLine(i + " done"); 
} 

隨着任務你有更好的方法來控制你的工作項目,並擴大那些能夠提高性能的選項。任務的默認TaskScheduler使用ThreadPool排隊工作項目。 (請閱讀本答案的底部以獲取有關任務的更多信息。)

所以,要回答這個問題,我們需要知道doWork實際上做了什麼:-)但總的來說Task將是一個不錯的選擇和一個很好的抽象。

並行foreach

如果使用循環產卵的工作,你正在做data parallelism然後並行的foreach可以做的工作:

Parallel.For(0, 500, i => doWork(i)); 

鏈接:

要評論從花錢

http://msdn.microsoft.com/en-us/library/dd537609.aspx

任務提供兩個主要好處:

1)更高效,更可擴展的系統資源的使用。

在幕後,任務排隊等待ThreadPool,這已被 增強,算法(如爬山)確定和 調整爲最大化吞吐量的線程數。這使得 任務相對輕量級,並且您可以創建其中的許多任務來啓用細粒度並行。爲了補充這一點,採用廣爲人知的工作竊取算法來提供負載平衡。

2)更多的線程或工作項目可能的編程控制。

任務和圍繞它們構建的框架提供了一套豐富的API集合,支持等待,取消,繼續,強大的異常 處理,詳細狀態,自定義調度等。

更新答案

嗯,這是不幸的是一個壞的API,因爲它不會讓你以異步方式做到這一點。它可能會變慢,因爲你同時啓動了很多連接(或者你開始太少)。與MaxDegreeOfParallelism

var jobs = new[] { 1, 2, 3}; 
var options = new ParallelOptions { MaxDegreeOfParallelism = 3 }; 
Parallel.ForEach(jobs, options, i => doWork(i)); 

與實驗:

試試這個。

+0

「任務並行庫將產生比ThreadPool更多的線程」。 TPL默認不使用ThreadPool?你可以發佈任何支持這一說法嗎? – spender 2012-02-19 12:03:04

+0

doessn't這意味着ThreadPool已經升級到允許任務運行更好?無論是否正在使用任務,變化都在那裏。 – spender 2012-02-19 12:08:08

+0

@spender其實我現在還不確定。它還表示這是「任務」的好處。我會調查:) – 2012-02-19 12:09:44

1

任何涉及爲要處理的ThreadPool排隊500個項目的任何操作都將無法以最佳吞吐量運行。 ThreadPool通常很不情願旋轉額外的線程,因爲這種用法不是設計者所期望的。

這聽起來像你是IO綁定,在這種情況下,你可以異步執行IO和服務一切與線程很少。但是,如果不瞭解更多關於工作量的信息,這是一個猜謎遊戲。

+0

這是爲了顯示目的我有一個最大的100個異步方法在當時需要完成,異步方法從網絡服務器下載數據(真正的API) – Svexo 2012-02-19 12:36:06