2013-02-28 74 views
14

我的应用程序需要将插件加载到单独的应用程序域中,然后异步执行它们中的某些代码。我已经写了一些代码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将永远不会返回。有任何想法吗?

+2

查看“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

+0

我无法重现这一点。 'ControllerAppDomain.SomethingAsync'永远不会挂在我身上,无论是在块上还是使用'await',无论是在线程池上下文还是单线程上下文中。你确定上面的代码重复了这个问题吗? – 2013-02-28 18:49:09

+0

@StephenCleary我只是试过另一台机器上的代码,无法在那里重现它。有趣。 – 2013-02-28 20:09:24

回答

2

我最好的猜测是,你所面临的这些问题,因为它包含等待异步方法的,这是通过线程池管理可分配一些回收线程

参考 Best practice to call ConfigureAwait for all server-side code

其实只是在做一个的await可以做到这一点(把你在不同的线程)。一旦异步方法处于等待状态,该方法将被阻止,但该线程将返回线程 池。当方法准备好继续时,线程池中的任何线程都被抓取到 并用于恢复该方法。

尝试精简代码,为基线情况生成线程,并且性能是最后一次。

+0

谁投下了这个答案,留下解释原因的评论会很有帮助。 – 2013-06-08 05:59:03

相关问题