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();
}

但是,此代码不会取消方法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

取消需要两种类型:一种是源,另一种是侦听器。即,您需要一个会触发取消的实例,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 回答