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



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

当下载失败时(例如由于网络连接),它会向 UI 发送一条消息。UI 通过显示消息对话框来处理此消息:

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

问题是,如果 UI 尝试在用户关闭第一条消息之前显示另一条消息,则对 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线程)任务?

谁能指出我正确的方向?

我想做的是序列化对此的访问

最简单的解决方案是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 的后台任务。

最新更新