我在c#中使用Task在多线程中通过FTP发送文件。
这是我的函数(文件是字符串列表)
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;
}
}
以这种方式,程序总是尝试保存列表的最后一个文件,但如果我添加一个Sleep(50),它会正确上传文件。 似乎程序启动4个任务做同样的工作(保存最后一个文件)只有我不使用睡眠,但我不明白为什么,我不知道如何解决问题。
有人能帮助我吗?谢谢
答案 0 :(得分: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++;
}
现在没有什么会改变copyJ
和copyF
- 你将在循环的每次迭代中获得一个单独的变量。在C#5中,您不需要copyF
,而只能使用f
。
...但我还建议您使用Task.Factory.StartNew()
,(或.NET 4.5中的Task.Run
)或Parallel.For
。