6

我正在C#中編寫下載程序,並停在以下問題:我應該使用什麼樣的方法來並行化我的下載並更新我的GUI?C#下載程序:我應該使用線程,BackgroundWorker還是ThreadPool?

在我第一次嘗試,我用4個線程,並在他們每個人的完成,我開始另一個問題:主要的問題是我的CPU在每一個新的線程開始進入100%。

周圍的Googling,我發現了BackgroundWorker的線程池和的存在:指出我想更新我與我下載的各個環節的進度GUI,什麼是最好的解決辦法?

1)創建4個不同的BackgroundWorker,一個代表附加到各事件ProgressChanged到功能在我的GUI來更新進度?

2)使用ThreadPool並將最大和最小線程數設置爲相同的值?

如果我選擇#2,當沒有更多的線程隊列中,它停止了4個工作線程?它會暫停嗎?由於我必須下載不同的鏈接列表(每個鏈接20個鏈接)並在完成時從一個鏈接移動到另一個鏈接,ThreadPool會在每個鏈表之間啓動和停止線程嗎?

如果我想改變帶電作業的線程數,並決定使用線程池,從10個線程變爲6,它拋出和異常並停止4周隨機的主題?

這是唯一讓我頭疼的部分。 我事先感謝你們每個人的回答。

+0

爲什麼不使用線程池? http://msdn.microsoft。com/en-us/library/3dasc8as%28v = vs.80%29.aspx#Y23 – Stormenet

回答

10

我會建議使用WebClient.DownloadFileAsync這一點。您可以進行多次下載,每次下載都會引發DownloadProgressChanged事件,並在完成時提升DownloadFileCompleted

您可以通過使用一個隊列,信號燈控制併發或者,如果你使用.NET 4.0,一個BlockingCollection。例如:

// Information used in callbacks. 
class DownloadArgs 
{ 
    public readonly string Url; 
    public readonly string Filename; 
    public readonly WebClient Client; 
    public DownloadArgs(string u, string f, WebClient c) 
    { 
     Url = u; 
     Filename = f; 
     Client = c; 
    } 
} 

const int MaxClients = 4; 

// create a queue that allows the max items 
BlockingCollection<WebClient> ClientQueue = new BlockingCollection<WebClient>(MaxClients); 

// queue of urls to be downloaded (unbounded) 
Queue<string> UrlQueue = new Queue<string>(); 

// create four WebClient instances and put them into the queue 
for (int i = 0; i < MaxClients; ++i) 
{ 
    var cli = new WebClient(); 
    cli.DownloadProgressChanged += DownloadProgressChanged; 
    cli.DownloadFileCompleted += DownloadFileCompleted; 
    ClientQueue.Add(cli); 
} 

// Fill the UrlQueue here 

// Now go until the UrlQueue is empty 
while (UrlQueue.Count > 0) 
{ 
    WebClient cli = ClientQueue.Take(); // blocks if there is no client available 
    string url = UrlQueue.Dequeue(); 
    string fname = CreateOutputFilename(url); // or however you get the output file name 
    cli.DownloadFileAsync(new Uri(url), fname, 
     new DownloadArgs(url, fname, cli)); 
} 


void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) 
{ 
    DownloadArgs args = (DownloadArgs)e.UserState; 
    // Do status updates for this download 
} 

void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) 
{ 
    DownloadArgs args = (DownloadArgs)e.UserState; 
    // do whatever UI updates 

    // now put this client back into the queue 
    ClientQueue.Add(args.Client); 
} 

沒有必要顯式管理線程或轉到TPL。

+0

我覺得線ClientQueue.Add(新的WebClient());是錯誤的,應該是ClientQueue.Add(cli)。無論如何,我認爲這個方法有兩個問題:1)我必須在下載它之前指定文件名,但是我不知道它的名字。我通常從http頭響應的「Content-Disposition」鏈接中獲取鏈接的名稱。 2)我第一次寫我的應用程序,在可用的選項中,有WebClient,但是如果我沒有記錯的話,它會在每個鏈接的後臺打開InternetExplorer!我仍然記得那個無處不在的彈出... – DDB

+0

我修復了你確定的錯誤。 'WebClient'不打開IE。你可能會想到'WebBrowser'控件。 「WebClient」是「HttpWebRequest」和「HttpWebResponse」的包裝。如果你需要頭文件中的信息,你可以從'ResponseHeaders'屬性中獲取它們。以上只是一個例子。通過做出一些小改動可以輕鬆滿足您的要求。 –

+0

雖然使用顯式線程可以工作,但爲每個下載專門分配一個線程是非常浪費的。而TPL可能做得不好。在良好的連接上,可以運行十幾個或更多的併發下載,每個下載都將是一個花費大部分時間等待數據的單獨線程。與使用'DownloadFileAsync'相反,DownloadFileAsync只會分配儘可能多的線程來處理下載的數據。 –

0

100%的CPU負載與下載無關(因爲您的網絡實際上始終是瓶頸)。我會說你必須檢查你的邏輯如何等待下載完成。

你可以發表多次啓動線程代碼的代碼嗎?

+0

它與代碼本身無關,但創建新線程的事實確實使用cpu(我對100 %cpu,它只是在創建線程的時間[瞬間]更多的40-50%,這是正常的[我在舊的Turion64bit 1.8GHz,單核心,所以我注意到這些濫用cpu)和創建和銷燬線程是cpu和ram的浪費,因爲它們可以被重用:我想知道什麼是「最佳」解決方案。 – DDB

4

我想你應該考慮使用Task Parallel Library,這在.NET 4是新的,是專爲解決這些類型的問題

+0

我可能會用同一條路線上的另一個解決方案修正此問題嗎?具有parallel.foreach的背景工作者(url,url => {/ * do action * /});在裏面。 - 更容易閱讀(比如foreach),並允許邏輯在BGW運行時繼續。 –

+0

看來,MaxDegreeOfParallelism允許我設置線程/任務的最大數量,但不是最小數量。不僅如此,但似乎我不能在現場改變這個價值。好的建議,但。 – DDB

0

通過創建4個不同的backgroundworkers您將創建單獨的線程將不再幹涉與你的GUI。背景工作者很容易實現,從我的理解中可以完全滿足您的需求。

就我個人而言,我會這樣做,只是讓其他人不能啓動,直到前一個完成。 (或者,也許只是一個,並允許它在同一時間以正確的順序執行一個方法。)

FYI - Backgroundworker

+0

使用'BackgroundWorker'不會創建新的進程,而是在線程池線程上執行。他不會創建單獨的進程,而是創建單獨的線程。 –

+0

編輯更正。有條款困惑 – sealz

相關問題