Reputation: 1358
I'm using Task in c# to send file via FTP in multithread.
here is my function (file is a list of strings)
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);
and the function to upload files
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;
}
}
in this way the program try always to save the last file of the list, but if I add a Sleep(50) it uploads the files correctly. it seems that the program starts 4 task doing the same job (saving the last file) only if I do not use sleep, but I do not understand why and I don't know how to solve the problem.
Can someone help me? thank you
Upvotes: 3
Views: 409
Reputation: 1503789
Look at your code:
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++;
}
The lambda expression uses the current value of j
whenever it's executed. So if the task starts after the j
is incremented, you'll miss the value you intended.
In C# 4, you have the same problem with f
- but this has been fixed in C# 5. See Eric Lippert's blog post "Closing over the loop variable considered harmful" for more details.
The smallest fix is trivial:
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++;
}
Now nothing will change copyJ
and copyF
- you'll get a separate variable being captured in each iteration of the loop. In C# 5, you don't need copyF
, and can just use f
instead.
... but I'd also suggest using Task.Factory.StartNew()
, (or Task.Run
in .NET 4.5) or just Parallel.For
.
Upvotes: 9