andrea
andrea

Reputation: 1358

c# Task do not work without sleep between them

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

Answers (1)

Jon Skeet
Jon Skeet

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

Related Questions