我的应用程序需要将插件加载到单独的应用程序域中,然后异步执行它们中的某些代码。我已经写了一些代码marshallable类型的包Task
:合并应用程序域远程处理和任务时发生死锁
static class RemoteTask
{
public static async Task<T> ClientComplete<T>(RemoteTask<T> remoteTask,
CancellationToken cancellationToken)
{
T result;
using (cancellationToken.Register(remoteTask.Cancel))
{
RemoteTaskCompletionSource<T> tcs = new RemoteTaskCompletionSource<T>();
remoteTask.Complete(tcs);
result = await tcs.Task;
}
await Task.Yield(); // HACK!!
return result;
}
public static RemoteTask<T> ServerStart<T>(Func<CancellationToken, Task<T>> func)
{
return new RemoteTask<T>(func);
}
}
class RemoteTask<T> : MarshalByRefObject
{
readonly CancellationTokenSource cts = new CancellationTokenSource();
readonly Task<T> task;
internal RemoteTask(Func<CancellationToken, Task<T>> starter)
{
this.task = starter(cts.Token);
}
internal void Complete(RemoteTaskCompletionSource<T> tcs)
{
task.ContinueWith(t =>
{
if (t.IsFaulted)
{
tcs.TrySetException(t.Exception);
}
else if (t.IsCanceled)
{
tcs.TrySetCancelled();
}
else
{
tcs.TrySetResult(t.Result);
}
}, TaskContinuationOptions.ExecuteSynchronously);
}
internal void Cancel()
{
cts.Cancel();
}
}
class RemoteTaskCompletionSource<T> : MarshalByRefObject
{
readonly TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
public bool TrySetResult(T result) { return tcs.TrySetResult(result); }
public bool TrySetCancelled() { return tcs.TrySetCanceled(); }
public bool TrySetException(Exception ex) { return tcs.TrySetException(ex); }
public Task<T> Task
{
get
{
return tcs.Task;
}
}
}
它使用这样的:
sealed class ControllerAppDomain
{
PluginAppDomain plugin;
public Task<int> SomethingAsync()
{
return RemoteTask.ClientComplete(plugin.SomethingAsync(), CancellationToken.None);
}
}
sealed class PluginAppDomain : MarshalByRefObject
{
public RemoteTask<int> SomethingAsync()
{
return RemoteTask.ServerStart(async cts =>
{
cts.ThrowIfCancellationRequested();
return 1;
});
}
}
但我已经遇到了障碍。如果你看看ClientComplete
,我已经插入了Task.Yield()
。如果我评论这条线,ClientComplete
将永远不会返回。有任何想法吗?
查看“c#async deadlock ConfigureAwait”的搜索结果,如http://stackoverflow.com/questions/13489065/best-practice-to-call-configureawait-for-all-server-side-code,因为我认为它将是一个解决方案。 – 2013-02-28 18:29:28
我无法重现这一点。 'ControllerAppDomain.SomethingAsync'永远不会挂在我身上,无论是在块上还是使用'await',无论是在线程池上下文还是单线程上下文中。你确定上面的代码重复了这个问题吗? – 2013-02-28 18:49:09
@StephenCleary我只是试过另一台机器上的代码,无法在那里重现它。有趣。 – 2013-02-28 20:09:24