如何按FIFO顺序序列化等待任务

时间:2013-12-16 23:00:20

标签: c# windows-runtime async-await

在我的WinRT应用程序中,UI允许用户单击列表项以开始从服务器下载预览。当列表项处于此下载状态时,列表项模板中将显示进度条。因此,UI不会被阻止,用户可以自由地点击其他项目并开始(并发)下载。

当下载失败时(由于网络连接,让我们说),它会向UI发送一条消息。 UI通过显示MessageDialog:

来处理此消息
public async void ShowError(string message)
{
    var dialog = new MessageDialog(message, "Error")
    {
        CancelCommandIndex = 0,
        DefaultCommandIndex = 0,
        Options = MessageDialogOptions.None
    };
    dialog.Commands.Add(new UICommand("OK"));

    await dialog.ShowAsync();
}

问题是,如果用户界面在用户取消第一条消息之前尝试显示另一条消息,则对MessageDialog.ShowAsync的调用会抛出System.UnauthorizedAccessException

我想要做的是序列化对此的访问,以便第二条消息等待第一条消息,然后显示。第三条消息将等待第二条消息,依此类推。

所有这一切都发生在UI线程上,我不知道如何正确地做到这一点。似乎我希望每个新的错误消息框请求将自己设置为“当前”请求,然后等待解雇“上一个”:

public async void ShowError(string message)
{
    ...

    // *** PSEUDO CODE FOLLOWS ***

    // If a message is being displayed...
    if (_currentTask != null)
    {
        // ...wait for that task to complete
        _currentTask = NewTaskThatWaitsFor(_currentTask);
        await _currentTask;
    }
    _currentTask = dialog.ShowAsync();
    await currentTask;
}

我如何在我之前等待的另一个(UI线程)任务中'附加'自己等待(不阻止 - 这是UI线程)?

有人能指出我正确的方向吗?

1 个答案:

答案 0 :(得分:3)

  

我想要做的是序列化对此

的访问

最简单的解决方案是SemaphoreSlim

private readonly SemaphoreSlim _dialogMutex = new SemaphoreSlim(1);
public async void ShowError(string message)
{
    await _dialogMutex.WaitAsync();
    try
    {
        var dialog = new MessageDialog(message, "Error")
        {
            CancelCommandIndex = 0,
            DefaultCommandIndex = 0,
            Options = MessageDialogOptions.None
        };
        dialog.Commands.Add(new UICommand("OK"));

        await dialog.ShowAsync();
    }
    finally
    {
        _dialogMutex.Release();
    }
}

但是,由于我已经写完了,我建议你重新考虑一下这个部分:

  

当下载失败时(由于网络连接,让我们说),它会向UI发送一条消息。 UI处理此消息...

如果您的后台任务完全不知道UI,我发现代码更清晰。如果您正在使用消息总线,那很好(尽管我从未在需要消息总线的UI应用程序上工作过);但是你要避免的是后台任务直接向UI发送消息。