如何使用async/await实现命令模式

3

我目前正在升级一些现有的代码,以供Windows Universal使用,并且正在尝试将一个命令模式转换为与新的async/await功能配合使用。

我有一个命令调度程序类,它在自己的线程中运行并处理已添加到其队列中的命令。涉及到的方法如下:

private List<ICommandItem> _items;

private void ProcessCommands()
{
    while(_items.count > 0)
    {
        _items[0].Execute();
        _items.RemoveAt(0);
    }
}

我的问题是,我的一些ICommandItem.Execute()方法现在需要异步执行,因为它们涉及文件I / O,而其他方法则没有异步方法。我该如何修改上述模式,使得:
  1. 我的执行器可以处理异步和非异步的ICommandItems
  2. 仅在前一个命令完成后,执行器才开始执行命令。
我很乐意只同步执行方法,但这现在会导致死锁。
2个回答

9
我的问题是,我的一些ICommandItem.Execute()方法现在需要异步,因为它们涉及文件输入输出,而其他方法没有异步方法。从技术上讲,异步(返回Task)的签名只意味着实现可能是异步的。因此,虽然可以引入单独的异步接口,但另一种方法是将现有接口更改为返回Task。
interface ICommandItem
{
  Task ExecuteAsync(); // Used to be "void Execute();"
}

你的同步实现将需要更改为返回Task

Task ICommandItem.ExecuteAsync()
{
  // Do synchronous work.
  return Task.CompletedTask; // If you're not on 4.6 yet, you can use "Task.FromResult(0)"
}

虽然异步实现很简单:

async Task ICommandItem.ExecuteAsync()
{
  await ...; // Do asynchronous work.
}

你的“runner”可以使用 await

private async Task ProcessCommandsAsync()
{
  while(_items.count > 0)
  {
    await _items[0].ExecuteAsync();
    _items.RemoveAt(0);
  }
}

0

你可以提供第二个接口,IAsyncCommandItem:

public interface IAsyncCommandItem : ICommandItem
{
}

在你的 while 循环中,检查该项是否实现了这个接口,并进行处理:
private async void ProcessCommands()
{
    while(_items.count > 0)
    {
        var command = _items[0] as IAsyncCommandItem;
        if (command != null)
        {
            await command.Execute();
        }
        else
        {
            _items[0].Execute();
        }
        _items.RemoveAt(0);
    }
}

当然,这假定您有自由修改特定命令类以实现新接口的能力。

1
问题在于两个Execute方法的返回类型不同(一个返回Task)。我想我可以让IAsyncCommandItem和ICommandItem从一个共同的父接口派生,但我希望有一个更优雅的解决方案。 - Daz Eddy
@DazEddy - 两者返回的结果是相同的。使用await会将Task转换为实际返回的项。 - Erik Funkenbusch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接