2013-02-28 19 views
14

Moja aplikacja musi ładować wtyczki do oddzielnych domen aplikacji, a następnie wykonać asynchronicznie kod wewnątrz nich. Pisałem jakiś kod zawinąć Task w marshallable typów:Zakleszczenie podczas łączenia funkcji zdalnej domeny aplikacji i zadań

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; 
     } 
    } 
} 

Jest używany jak:

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; 
     }); 
    } 
} 

Ale ja napotkasz niepowodzeniem. Jeśli spojrzysz w ClientComplete, wstawiłem Task.Yield(). Jeśli skomentuję tę linię, ClientComplete nigdy nie wróci. Jakieś pomysły?

+2

Sprawdź wyniki wyszukiwania dla "C# async deadlock ConfigureAwait", np. Http://stackoverflow.com/questions/13489065/best-practice-to-call-configureawait- for-all-server-side-code, jak myślę byłoby rozwiązaniem. –

+0

Nie mogę tego zaimponować. 'ControllerAppDomain.SomethingAsync' nigdy nie zawiesza się dla mnie, bez względu na to, czy go blokuję, czy też używam opcji" czekaj ", czy to w kontekście puli wątków, czy w kontekście pojedynczego wątku. Czy na pewno powyższy kod duplikuje problem? –

+0

@StephenCleary Właśnie wypróbowałem kod na innym komputerze i nie mogę go tam odtworzyć. Ciekawy. –

Odpowiedz

2

Mój najlepszy przypuszczenie to, że stoją w obliczu tych problemów, ze względu na metody asynchronicznym który zawiera czekają a ten jest zarządzany przez puli wątków które mogą przeznaczyć pewną wątek recyklingu.

referencyjny Best practice to call ConfigureAwait for all server-side code

Właściwie tylko robi czekają może to zrobić (umieścić na innym wątku). Po oczekiwaniu na metodę asynchroniczną , metoda jest blokowana, ale wątek wraca do puli wątku . Gdy metoda jest gotowa do kontynuowania, każdy wątek jest wyrywkowywany z puli wątków i używany do wznowienia metody.

Spróbuj usprawnić kod, wygenerować wątki dla przypadków bazowych, a wydajność jest ostatnia.

+0

Ktokolwiek głosował za tą odpowiedzią, byłoby pomocne pozostawienie komentarza wyjaśniającego dlaczego. –