我正在尝试理解异步操作,我有点困惑。 行动只是荣耀代表。鉴于行动
Action act = null;
act += () => { Console.WriteLine("Sync"); };
act += async () => { await File.AppendAllLinesAsync("C:/Test.Txt",
new[] { "Async File Operation" });
};
我们如何调用此异步,因为其中一个委托是异步而另一个不是。我已经看到其他SO answers中的一些扩展方法被简化为示例,如下所示:
public static void InvokeAsync(this Action action, AsyncCallback ar, object userObject = null)
{
var listeners = action.GetInvocationList();
foreach (var t in listeners)
{
var handler = (Action)t;
handler.BeginInvoke(ar, userObject);
}
}
我担心这是否有效,因为它看起来像是为每个听众调用你的回调而没有意义。
我只是使用了更友好的版本async / await的异步,所以我不太了解这种语法。 (我假设回调将是await之后的所有内容,而userObject相当于在没有ConfigureAwait(false)的情况下调用sync时导致死锁的可怕SyncronizationContext,但这只是一个猜测)
这是语法不方便所以我会使用async await语法,因为使用duck-typing调用async / await。我已经阅读了一篇关于使用async with delegates的博客,例如
public static class DelegateExtensions
{
public static TaskAwaiter GetAwaiter(this Action action)
{
Task task = new Task(action);
task.Start();
return task.GetAwaiter();
}
}
由于某些原因,这也让我感到担忧,这看起来很像反模式 这不仅仅是创建一个将在单独的线程上同步运行我的动作的任务吗?我也没有看到这通过调用列表。
这些方法中的任何一种都适合异步调用运行委托吗? 有没有一种方法可以使用await语法调用异步委托,同时仍然完全利用异步? 在调用列表中调用具有多个函数的异步委托的正确方法是什么?
答案 0 :(得分:0)
我认为Eric Lippert的评论比以往任何时候都更能澄清情况。
总的来说,如果您需要对方法的返回类型执行操作,则不应使用多播委托。如果您仍然需要,至少使用Func<Task>
签名,则可以使用GetInvocationList
,as explained here对每个代表进行迭代。
但使用async void
方法是否真的无法以多播代理的方式工作?
事实证明,您可以通过使用自定义同步上下文并覆盖async void
和OperationStarted
方法来通知OperationCompleted
方法的开头和结尾。我们还可以覆盖Post
方法来设置子操作的同步上下文,以捕获后续的async void
次调用。
将它拼凑在一起,你可以得到类似的东西:
class Program
{
static async Task Main(string[] args)
{
Action act = null;
act += () => { Console.WriteLine("Sync"); };
act += async () =>
{
Callback();
await Task.Delay(1000);
Console.WriteLine("Async");
};
await AwaitAction(act);
Console.WriteLine("Done");
Console.ReadLine();
}
static async void Callback()
{
await Task.Delay(2000);
Console.WriteLine("Async2");
}
static Task AwaitAction(Action action)
{
var delegates = action.GetInvocationList();
var oldSynchronizationContext = SynchronizationContext.Current;
var asyncVoidSynchronizationContext = new AsyncVoidSynchronizationContext();
try
{
SynchronizationContext.SetSynchronizationContext(asyncVoidSynchronizationContext);
var tasks = new Task[delegates.Length];
for (int i = 0; i < delegates.Length; i++)
{
((Action)delegates[i]).Invoke();
tasks[i] = asyncVoidSynchronizationContext.GetTaskForLastOperation();
}
return Task.WhenAll(tasks);
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext);
}
}
}
public class AsyncVoidSynchronizationContext : SynchronizationContext
{
private TaskCompletionSource<object> _tcs;
private Task _latestTask;
private int _operationCount;
public Task GetTaskForLastOperation()
{
if (_latestTask != null)
{
var task = _latestTask;
_latestTask = null;
return task;
}
return Task.CompletedTask;
}
public override void Post(SendOrPostCallback d, object state)
{
Task.Run(() =>
{
SynchronizationContext.SetSynchronizationContext(this);
d(state);
});
}
public override void OperationStarted()
{
if (Interlocked.Increment(ref _operationCount) == 1)
{
// First operation
_tcs = new TaskCompletionSource<object>();
_latestTask = _tcs.Task;
}
base.OperationStarted();
}
public override void OperationCompleted()
{
if (Interlocked.Decrement(ref _operationCount) == 0)
{
// Last operation
_tcs.TrySetResult(null);
}
base.OperationCompleted();
}
}
输出结果为:
同步
异步
Async2
完成
当然,此代码仅用于娱乐目的。存在很多限制,例如如果您已经在使用同步它将无法正常工作这一事实上下文(例如WPF之一)。我也确定它在这里和那里都有一些微妙的错误和并发问题。