为什么异步方法调用程序行为奇怪?

时间:2014-02-28 11:09:10

标签: c# asynchronous async-await

我正在开发一个Windows 8.1商店应用,用户可以在其中将文本保存到文件中。

我一直在努力了解如何最好地使用async并等待。

这就是我想出来的:

private async void userText_KeyDown(object sender, KeyRoutedEventArgs e)
    {            
        if (e.Key == Windows.System.VirtualKey.Enter)
        {
            if (addUserImput)
            {
                userStringlist.Add(userBox.Text);
                userBox.Text = "";
                addUserImput = false;
            }
            await WriteToFileAsync();
            addUserImput = true;
        }
    }

async-method看起来像这样:

private async Task WriteToFileAsync()
    {
        string name = "userStrings.txt";
        var option = CreationCollisionOption.ReplaceExisting;
        var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
        var file = await folder.CreateFileAsync(name, option);
        await Windows.Storage.FileIO.WriteLinesAsync(file, userStringlist);            
    }

只要WriteToFileAsync到达await-keyword,执行就会重新开始。为了防止列表中的重复,我必须添加if语句。

这让我觉得很奇怪。我还是新手,所以我可能会错过一些东西。为什么keydown事件从顶部恢复,做了已经完成的工作?

我的“解决方法”有效,我只是不了解事件行为背后的逻辑。

2 个答案:

答案 0 :(得分:2)

是的,这就是异步解决方案的工作原理。当你点击你的第一个实际异步操作(在这种情况下,CreateFileAsync)时,该方法返回其调用者,该调用者返回其调用者,并最终使其完全脱离整个方法并备份到应用程序的消息循环。然后继续处理其他UI消息。其中一些消息可能是按键事件(它们最终可能在异步操作完成之前运行)。其他事件可能是绘制事件或鼠标单击事件,使您的表单可以执行与用户交互所需的任何事情。这就是防止它冻结的原因。

您要做的是防止同时运行您的代码段。如果这不是异步,那么通常使用lock关键字来解决这个问题,但这不是异步方法的选项。您需要的是一些阻止访问代码的方法,直到该代码块的任何其他执行完成。幸运的是,工具可用于执行此操作。您可以 使用布尔值,但这有点脆弱,并且随着应用程序复杂性的增长而容易出错。信号量是专门为此任务设计的:

private SemaphoreSlim semaphore = new SemaphoreSlim(1);
private async void Bar()
{
    try
    {
        await semaphore.WaitAsync();
        //do stuff
    }
    finally
    {
        semaphore.Release();
    }
}

SemaphoreSlim类有一个WaitAsync方法专门设计用于异步方法,例如你的方法。您可以等到信号量空闲,执行代码,然后确保在完成时释放信号量,以便其他代码可以移动到代码块中。

答案 1 :(得分:0)

在这种情况下,您可能需要使用processed = true。检查http://msdn.microsoft.com/en-us/library/system.windows.forms.keyeventargs.handled(v=vs.110).aspx是否有效