问题
i'm currently upgrading some existing code for use by windows universal and am struggling to convert a command pattern to work with the new async/await functionality.
I have a command scheduler class that runs within its own thread and processes commands that have been added to its queue. The method in question looks like this:
private List<ICommandItem> _items;
private void ProcessCommands()
{
while(_items.count > 0)
{
_items[0].Execute();
_items.RemoveAt(0);
}
}
My problem is that some of my ICommandItem.Execute() methods now need to be async because they involve file io whilst others have no async methods. How could I modify the above pattern so that:
- my executor can handle both async and non-async ICommandItems
- The executor only starts execution of a command once the previous command has completed.
I would be happy to just execute the methods synchronously but that now causes deadlocks everywhere.
回答1:
My problem is that some of my ICommandItem.Execute() methods now need to be async because they involve file io whilst others have no async methods.
Technically, an asynchronous (Task
-returning) signature only means that the implementation may be asynchronous. So, while you can introduce a separate asynchronous interface, another approach is to change the existing interface to be Task
-returning:
interface ICommandItem
{
Task ExecuteAsync(); // Used to be "void Execute();"
}
Your synchronous implementations will have to change to return a Task
:
Task ICommandItem.ExecuteAsync()
{
// Do synchronous work.
return Task.CompletedTask; // If you're not on 4.6 yet, you can use "Task.FromResult(0)"
}
While asynchronous implementations are straightforward:
async Task ICommandItem.ExecuteAsync()
{
await ...; // Do asynchronous work.
}
And your "runner" can just use await
:
private async Task ProcessCommandsAsync()
{
while(_items.count > 0)
{
await _items[0].ExecuteAsync();
_items.RemoveAt(0);
}
}
回答2:
You could provide a second interface, IAsyncCommandItem:
public interface IAsyncCommandItem : ICommandItem
{
}
In your while loop, check to see if the item implements this interface, and handle it:
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);
}
}
This assumes, of course, that you have the freedom to modify specific command classes to implement the new interface.
来源:https://stackoverflow.com/questions/33784841/how-to-implement-command-pattern-with-async-await