2013-02-28 12 views
14

La mia app deve caricare plug-in in domini app separati e quindi eseguire codice all'interno di essi in modo asincrono. Ho scritto un codice per avvolgere Task nei tipi marshallable:Deadlock durante la combinazione di dominio remoto e attività

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

E 'usato come:

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

Ma ho incontrato un intoppo. Se guardi su ClientComplete, c'è uno Task.Yield() che ho inserito. Se commento questa riga, ClientComplete non tornerà mai più. Qualche idea?

+2

Controlla i risultati di ricerca per "C# async deadlock ConfigureAwait" come http://stackoverflow.com/questions/13489065/best-practice-to-call-configureawait-for-all-server-side-code come penso sarebbe una soluzione. –

+0

Non riesco a ripeterlo. 'ControllerAppDomain.SomethingAsync' non si blocca mai per me, sia che lo blocchi o usi' await', sia in un contesto di pool di thread o in un contesto a thread singolo. Sei sicuro che il codice sopra duplica il problema? –

+0

@StephenCleary Ho appena provato il codice su un'altra macchina e non riesco a riprodurlo neanche lì. Interessante. –

risposta

2

La mia ipotesi migliore è che si trovano ad affrontare questi problemi a causa del metodo di asincrono che contiene attendono e questo è gestito tramite l'ThreadPool che può allocare alcune Discussione riciclato.

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

In realtà, solo facendo un await può farlo (si mette su un thread diverso). Una volta che il metodo asincrono tocca un atteso, il metodo viene bloccato ma il thread ritorna al pool di thread . Quando il metodo è pronto per continuare, qualsiasi thread viene strappato dal pool di thread e utilizzato per riprendere il metodo.

Provare a semplificare il codice, generare thread per i casi di base e la prestazione è l'ultima.

+0

Chiunque abbia votato questa risposta, sarebbe utile lasciare un commento che spieghi perché. –

Problemi correlati