1

私は非同期待機操作を学んでおり、非常に役立つ記事を見つけました。

この記事の最後のコード スニペットを検討します。

public async Task ProcessWriteMult(CancellationToken cancellationToken)
{
string folder = @"tempfolder\";
List<Task> tasks = new List<Task>();
List<FileStream> sourceStreams = new List<FileStream>();

try
{
    for (int index = 1; index <= 10; index++)
    {
        string text = "In file " + index.ToString() + "\r\n";

        string fileName = "thefile" + index.ToString("00") + ".txt";
        string filePath = folder + fileName;

        byte[] encodedText = Encoding.Unicode.GetBytes(text);

        FileStream sourceStream = new FileStream(filePath,
            FileMode.Append, FileAccess.Write, FileShare.None,
            bufferSize: 4096, useAsync: true);

        Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
        sourceStreams.Add(sourceStream);

        tasks.Add(theTask);
    }

    await Task.WhenAll(tasks);
}

finally
{
    foreach (FileStream sourceStream in sourceStreams)
    {
        sourceStream.Close();
    }
}
}

タスクをキャンセルするコードをいくつか試しましたが、私見ですが、これらは非常に醜い試みです。この試みでは、プログラムフローはユーザーがキーを押すのを待ちますが、イベントのように、ユーザーが Esc ボタンを押すまでこのコードを機能させたいと思います:

if(Console.ReadKey().Key==System.ConsoleKey.Escape)
    cts.Cancel();

Escape キーで適切にキャンセルするにはどうすればよいですか?

アップデート:

このコードを挿入しました:

static void Main(string[] args)
{
    var cts=new CancellationTokenSource();
    ProcessWriteMult(cts);
    if(Console.ReadKey().Key == System.ConsoleKey.Escape)
        cts.Cancel();
}

ただし、このコードは method をキャンセルしませんProcessWriteMult(cts)。私は何を間違っていますか?

4

2 に答える 2

3

まず、使用しないでくださいasync void。MSDN の例が悪い習慣を助長する理由はわかりませんが、そうしないでください。メソッドを使用async voidすると、メソッドが「起動して忘れる」ことを意味し、非同期的に待機することはできず、その内部で発生した例外はスレッドプールスレッドでスローされます。代わりに、を使用してasync Taskください。

現在、.NET 4.0 では の概念が導入されており、協調的なキャンセルCancellationTokenSourceを行うのに役立ちます。これは、「外部」の誰かがキャンセルを要求したことを知るために、トークンを監視する必要があることを意味します。これは次のように行うことができます。

try
{
    for (int index = 1; index <= 10; index++)
    {
        // Monitor the token, you decide where.
        cancellationToken.ThrowIfCancellationRequested();
        string text = "In file " + index.ToString() + "\r\n";

        string fileName = "thefile" + index.ToString("00") + ".txt";
        string filePath = folder + fileName;

        byte[] encodedText = Encoding.Unicode.GetBytes(text);

        FileStream sourceStream = new FileStream(filePath,
            FileMode.Append, FileAccess.Write, FileShare.None,
            bufferSize: 4096, useAsync: true);

        Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
        sourceStreams.Add(sourceStream);

        tasks.Add(theTask);
    }

    await Task.WhenAll(tasks);
}

ただし、このコードはメソッド ProcessWriteMult(cts) をキャンセルしません。私は何を間違っていますか?

エスケープを押すまでに、メソッドの実行がすでに終了している可能性があります。これをテストするTask.Delayには、一定の間隔 (たとえば 5 秒) 待機してから、キャンセル トークンを監視します。

于 2015-06-23T06:29:05.670 に答える
1

キャンセルには 2 つのタイプが必要です。1 つはソース、もう 1 つはリスナーです。つまり、キャンセルをトリガーするインスタンスが必要でありCancellationTokenSource、コードはトークンを介してキャンセルをリッスンする必要があります。

Task ProcessWriteMultAsync(CancellationTokenSource source)
{
  // ...
}

var cts = new CancellationTokenSource();
await ProcessWriteMultAsync(cts.Token);
if(Console.ReadKey().Key==System.ConsoleKey.Escape)
           cts.Cancell();

そして、使用しないでくださいasync void!これらは、イベント ハンドラーにのみ使用する必要があります。

于 2015-06-23T06:21:07.633 に答える