2012-11-07 24 views
3

我在c#中使用任務通過FTP在多線程中發送文件。c#任務不工作,沒有他們之間的睡眠

這裏是我的功能(文件是一個字符串列表)

Task<bool>[] result = new Task<bool>[file.Count]; 
     int j = 0; 
     foreach (string f in file) 
     { 
      result[j] = new Task<bool>(() => ftp.UploadFtp(f, "C:\\Prova\\" + f + ".txt", j)); 
      result[j].Start(); 
      j++; 

      //System.Threading.Thread.Sleep(50); 

     } 
     Task.WaitAll(result, 10000); 

和功能,這樣的程序試圖始終保存的最後一個文件上傳文件

public static bool UploadFtp(string uploadFileName, string localFileName, int i) 
    { 
     FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://127.0.0.1/" + uploadFileName + ".txt"); 
     //settare il percorso per il file da uplodare 
     //FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://desk.txt.it/"); 
     request.Method = WebRequestMethods.Ftp.UploadFile; 

     request.Credentials = new NetworkCredential("ftp_admin", ""); 
     //request.Credentials = new NetworkCredential("avio", "avio_txt"); 
     try 
     { 
      Console.WriteLine(uploadFileName); 
      Console.WriteLine(i); 
      StreamReader sourceStream = new StreamReader(localFileName); 
      byte[] fileContents = File.ReadAllBytes(localFileName); 

      sourceStream.Close(); 
      request.ContentLength = fileContents.Length; 

      Stream requestStream = request.GetRequestStream(); 
      requestStream.Write(fileContents, 0, fileContents.Length); 
      requestStream.Close(); 

      FtpWebResponse response = (FtpWebResponse)request.GetResponse(); 

      //MessageBox.Show("Upload File Complete, status {0}", response.StatusDescription); 

      response.Close(); 
      return true; 
     } 
     catch (Exception e) 
     { 
      return false; 
     } 

    } 

列表,但如果我添加睡眠(50),則會正確上傳文件。 看來,程序啓動4任務做同樣的工作(保存最後一個文件)只有當我不使用睡眠,但我不明白爲什麼,我不知道如何解決問題。

有人可以幫助我嗎?謝謝

+3

嘗試在您的循環中添加一個'string localCopy = f;'語句,並使用localCopy而不是'f'。我懷疑你遇到了可怕的循環關閉問題,因爲關閉機制,你總是得到最後一個賦給'f'的引用。 – Tejs

+0

請注意,您的'UploadFtp'方法不是非常異步友好的 - 可以使IO異步,以便在上傳許多文件時實現更好的縮放。 – Lucero

+1

這是一個完美的例子,儘管異步開發變得越來越簡單,但仍然存在需要被理解爲正確執行的陷阱。幸運的是,在學習課程之前,通常只需要做錯一次。我知道這是第一隻手,因爲在我知道任何更好之前,我自己也做了同樣的事情:) –

回答

9

看看你的代碼:當它執行

int j = 0; 
foreach (string f in file) 
{ 
    result[j] = new Task<bool>(() => ftp.UploadFtp(f, "C:\\Prova\\" + f + ".txt", j)); 
    result[j].Start(); 
    j++; 
} 

Lambda表達式使用的j電流值。因此,如果在之後任務開始j增加,您將錯過您想要的值。

在C#4中,您遇到與f相同的問題 - 但這已在C#5中修復。有關更多詳細信息,請參閱Eric Lippert的博文"Closing over the loop variable considered harmful"

最小的解決辦法很繁瑣:

int j = 0; 
foreach (string f in file) 
{ 
    int copyJ = j; 
    string copyF = f; 
    result[j] = new Task<bool>(
     () => ftp.UploadFtp(copyF, "C:\\Prova\\" + copyF + ".txt", copyJ)); 
    result[j].Start(); 
    j++; 
} 

現在什麼都不會改變copyJcopyF - 你會得到一個單獨的變量作爲一個在每次循環抓獲。在C#5中,您不需要copyF,而只需使用f

...但我也建議使用Task.Factory.StartNew()(或.NET中的Task.Run)或只是Parallel.For

+0

謝謝!它現在有效! – andrea

+0

@Jon你真的需要一份'j'嗎?你正在設置它,但沒有使用它。 –

+0

@Jon哦...並祝賀50萬聲望點! –