3

マルチスレッドで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;
        }

    }

このようにして、プログラムは常にリストの最後のファイルを保存しようとしますが、Sleep(50)を追加すると、ファイルが正しくアップロードされます。スリープを使用しない場合にのみ、プログラムは同じジョブ(最後のファイルの保存)を実行する4つのタスクを開始するようですが、理由がわからず、問題の解決方法もわかりません。

誰かが私を助けることができますか?ありがとうございました

4

1 に答える 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++;
}

ラムダ式は、実行されるたびにの現在の値を使用します。jしたがって、がインクリメントされたjにタスクが開始されると、意図した値を見逃してしまいます。

C#4でも同じ問題が発生しますが、fこれはC#5で修正されています。詳細については、EricLippertのブログ投稿「有害と考えられるループ変数の終了」を参照してください。

最小の修正は簡単です。

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()...しかし、、 (またはTask.Run.NET 4.5では)または単にを使用することもお勧めしますParallel.For

于 2012-11-07T16:31:04.670 に答える