2013-01-14 17 views
5

Ho una classe di connessione che ha diversi metodi asincroni come SendText, SendImage eccUn contatore asincrono che può essere attendeva

La classe di collegamento ha un metodo Disconnect, e quando viene chiamato devo fare attenzione non iniziare a modificare lo stato interno della classe prima che tutti i metodi asincroni abbiano completato l'esecuzione.

Credo che un buon modo per ottenere questo è semplicemente mantenere un totale parziale del numero di operazioni in esecuzione, e poi quando voglio Disconnect posso semplicemente impostare Disconnecting = true e poi aspettare che il conteggio raggiunga 0

sto pensando a qualcosa sulla falsariga di questo

class ReferenceCounter 
{ 
    void Increment(); 

    void Decrement(); 

    async Task WaitForCounterToReachZero(); 
} 

Poi, quando un'operazione asincrona inizia potevo fare

refCounter.Increment(); 

quando si le estremità

refCounter.Decrement(); 

e all'interno del metodo Disconnect

disconnecting = true; 
taskCancellationSource.Cancel(); 
await refCounter.WaitForCounterToReachZero(); 
Cleanup(); 

Vi sono costruiti in classi .NET come questo?

O più importante per me, c'è un modo migliore per farlo?

Se fosse codice sincrono sarebbe stato così semplice come

lock (thisLock) 
{ 
    while (counter > 0) 
     Monitor.Wait(thisLock); 
} 

Ho appena trovato il costruito in classe CountdownEvent che fa la stessa cosa, ma non ha alcun metodo asincrono Wait né ha alcun evento, quindi dovrei bloccare.

risposta

4

Beh, partendo dal presupposto che non avrete mai incrementa ancora dopo si rendono 0, si poteva fare qualcosa di simile:

public class Latch 
{ 
    private int count = 0; 
    private readonly TaskCompletionSource<object> tcs = 
     new TaskCompletionSource<object>(); 

    public void Increment() 
    { 
     Interlocked.Increment(ref count); 
    } 

    public void Decrement() 
    { 
     if (Interlocked.Decrement(ref count) == 0) 
     { 
      tcs.TrySetValue(null); 
     } 
    } 

    public Task Task { get { return tcs.Task; } } 
} 

Quindi è possibile attendere someLatch.Task. In alternativa, si potrebbe fare il fermo stesso awaitable:

public TaskAwaiter GetAwaiter() 
{ 
    return tcs.Task.GetAwaiter(); 
} 

probabilmente si dovrebbe considerare come si desidera a guardia contro il "conteggio aumenta dopo scendendo a 0" aspetto thuogh - pensare a quello che ci si vuole che faccia . (Nel codice sopra, una volta impostato il valore del TCS, ulteriori attese si completeranno immediatamente.)

+0

Questa è una bella implementazione elegante di ciò che avevo in mente. Non devo preoccuparmi che il conteggio aumenti dopo essere sceso a 0 perché il server _disconnecting impedirà ulteriori operazioni. A parte questo, pensi che sia un buon modo di gestire un simile scenario? La mia preoccupazione è che la ragione per cui una tale classe non esista nel quadro è perché una cosa del genere sarebbe disapprovata. Ti sei trovato in una situazione simile e hai fatto le cose in modo diverso? – NoPyGod

+2

@NoPyGod: Non è del tutto chiaro se hai davvero bisogno di un conteggio - parli di avere un carico di metodi asincroni ... restituiscono tutti 'Task 'o qualcosa di simile? Se è così, 'Task.WhenAll' è tuo amico. È possibile mantenere un pool di attività in sospeso e attendere "Task.WhenAll" per attendere fino a quando non sono stati completati tutti. –

+0

Potrei farlo, ma all'interno del corpo del metodo asincrono non ho modo di accedere all'attività che verrà restituita.Per ottenere ciò che stai suggerendo, dovrei racchiudere un metodo interno che restituisce l'attività che posso quindi aggiungere a un gruppo di attività in sospeso. Questo sembra più elaborato di quello che sto suggerendo, ma se questa è una pratica consigliata per situazioni come queste, preferirei farlo in quel modo. Data la tua ovvia esperienza nella zona, sono molto interessato alla tua opinione. – NoPyGod

Problemi correlati