2014-10-05 74 views
2

我的應用程序必須連接到API並下載大約1200個項目。由於API的工作原理,我無法一次詢問所有項目,因此我必須爲每個項目發送一個查詢。項目很小,所以下載速度很快,如果我在短時間內發送太多請求(每2-3秒發出100個請求),服務器會阻止我(4-5秒)。我不希望服務器每次都阻止我的應用程序大約10-12次,所以我決定實現一個減速目標,另一個線程只需要休眠0.4秒,並且工作人員將加入它,所以如果工作人員完成每個線程下載0.4秒大關它會等待之前,如果它完成後,將直接進入下一個如何在BackgroundWorker中啓動線程?

期望的實現:

private void DownloadLibrary_DoWork(object sender, DoWorkEventArgs e) 
{ 
    int max = 1200; 
    int slowdown = 400; // milliseconds 

    Thread methronome = new Thread(() => Thread.Sleep(slowdown)); 

    for (int i = 0; i < max; i++) 
    { 
     methronome.Start(); 

     DownloadItem(i);    
     WorkerDownloadLibrary.ReportProgress((int)i/max, string.Format("Dowloaded item {0} out of {1}", i+1, max)); 

     methronome.Join(); 
    } 
} 

private void UserClickStart(object sender, RoutedEventArgs e) 
{ 
    System.Windows.MessageBox.Show("Let's start!"); 
    WorkerDownloadLibrary.RunWorkerAsync(); 
} 

private void DownloadLibrary_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    System.Windows.MessageBox.Show("Completed!"); 
} 

的問題是,如果我這樣做,後臺工作者提前結束(RunWorkerCompleted執行),如果我同時註釋methronome.Start()methronome.Join()這兩行,它會按預期工作,但當然會結束不時被封鎖。 我做錯了什麼?


我可以直接在BackgroundWorker的執行等待,因爲下載的速度非常快,無論如何,但似乎仍然不好而設計的,它會慢一個緩慢的電腦上太多。

工作,但落實緩慢:

private void DownloadLibrary_DoWork(object sender, DoWorkEventArgs e) 
{ 
    int max = 1200; 
    int slowdown = 400; // milliseconds 

    for (int i = 0; i < max; i++) 
    { 
     DownloadItem(i);    
     WorkerDownloadLibrary.ReportProgress((int)i/max, string.Format("Dowloaded item {0} out of {1}", i+1, max)); 
     Thread.Sleep(slowdown); 
    } 
} 

還有另一種方式來做到這一點:將所有查詢一個接一個,然後等待服務器塊只有當,但看起來很糟糕通過設計,當下載每2秒停止一次時,用戶就會頭暈目眩。而服務器可能會阻止我永遠如果我這樣做總是

工作,但普通的壞實現

private void DownloadLibrary_DoWork(object sender, DoWorkEventArgs e) 
{ 
    int max = 1200; 
    int slowdown = 5000; // milliseconds 

    string serverResponse; 
    for (int i = 0; i < max; i++) 
    { 
     do 
     { 
      serverResponse = DownloadItem(i); 

      if (serverResponse != "OK") 
       Thread.Sleep(slowdown); 

     } while (serverResponse != "OK") ; 

     WorkerDownloadLibrary.ReportProgress((int)i/max, string.Format("Dowloaded item {0} out of {1}", i+1, max)); 
    } 
} 

我如何能實現第一個選項?

+2

你有沒有試過用秒錶來一次你下載的執行,然後等待剩餘毫秒?這將是你的**工作的改進版本,但執行速度很慢**。使用額外的線程來做時間似乎對我來說過分。 – jessehouwing 2014-10-05 12:09:47

+0

您也可以嘗試讓數據提供商提供批量查詢選項,如果您要經常查詢它們,也可能會使它們更加快樂。 – jessehouwing 2014-10-05 12:10:52

+0

@BartoszKP它並不打算快。我解釋了這個問題:如果應用程序運行速度太快,API服務器會阻止我。這樣用戶可以看到「流暢」的下載,即使速度很慢。 @jessehouwing好主意!我會嘗試'秒錶'。關於詢問數據提供者,他們並不太在意API,文檔很糟糕,我過去要求提供技術問題,但他們的回答總是含糊不清而且沒用。我可以向你保證,他們不會把資源用於改進他們的API。也許將來當我的應用程序完成時,我會問他們關於「批量」選項 – 2014-10-05 12:17:22

回答

2

你的方法是行不通的,因爲as the documentation explains

一旦線程終止,它不能與其他呼叫開始重新啓動。

所以,簡單的解決辦法是,在每次重新創建線程:

for (int i = 0; i < max; i++) 
{ 
    Thread methronome = new Thread(() => Thread.Sleep(slowdown)); 
    methronome.Start(); 

    //.. 

但是,你爲什麼會想這樣做,目前還不清楚。代碼已經在另一個線程中運行,所以你的第二種方法,使用Thread.Sleep似乎很好。您可以通過測量是否真的需要等待所有時間(可能是某些下載時間稍長一些,因此無需總是等待所有400毫秒)來改進。類可以用來測量大塊時間(和線程不同,可以「重新啓動」)的便利類是Stopwatch類。這個想法(也發表在評論jessehouwing)看起來是這樣的:

int max = 1200; 
int slowdownMs = 400; 
Stopwatch sw = new Stopwatch(); 

for (int i = 0;i < max; ++i) 
{ 
    sw.Restart(); 

    DownloadItem(i);  
    ReportProgress(i, max);   

    sw.Stop(); 

    Thread.Sleep(Math.Max(0, (int)(slowdownMs - sw.ElapsedMilliseconds))); 
} 

//.... 
+1

這就是我的意思,我的意思是睡400ms的剩餘時間。 :)。雖然我懷疑你需要顯式地將'sw.ElapsedMilliseconds'轉換爲int,因爲它很長,Thread.Sleep不需要很長的時間。 – jessehouwing 2014-10-05 12:22:54

+0

@ jessehouwing謝謝,你是對的:) – BartoszKP 2014-10-05 12:24:34