2009-03-16 4 views
6

Mi sono imbattuto in una situazione piuttosto imbarazzante in un progetto al lavoro. Dobbiamo creare utenti su 4 o 5 servizi diversi e configurarlo in modo tale che se uno fallisce, falliscono tutti. Sono incapsulati all'interno di un blocco di ambito della transazione.Sincronizzazione di una chiamata asincrona in C#

Uno dei servizi di cui abbiamo bisogno per aggiungere utenti richiede il telneting e la fusione di alcuni dati. Ci sono altri modi per farlo (che costano soldi) ma per ora è quello con cui siamo bloccati. L'aggiunta di un utente richiede circa 3 minuti. Lavoreremo su come abbassarlo in modo significativo come si può immaginare ma non è proprio questo il punto. Questa chiamata è asincrona e deve funzionare in modo corretto. La battuta è, non ci può essere un massimo di 10 connessioni al servizio.

Il nostro progetto viene impostato per creare utenti in un batch. Quindi potenzialmente 50 utenti creati alla volta. Questo presenta un problema quando è possibile effettuare solo 10 connessioni tramite telnet e un utente elaborato non dovrebbe impiegare molto tempo oltre al servizio telnet. Ora devo sincronizzare questo processo in modo che il resto non possa continuare fino a quando non è terminato.

Utilizziamo callback e delegati con chiamate asincrone per implementare la funzionalità. Quale sarebbe il modo migliore per incapsulare la porzione asincrona e non continuare fino al completamento?

Dovremmo impostare un ciclo che termina solo al termine della chiamata? C'è qualcosa nella libreria di threading che potrebbe aiutare? Non ho mai lavorato con discussioni prima, quindi questa sarà la prima volta per me. Quali strumenti ci sono per aiutare con questo problema?

EDIT:

Se uso il/EndInvoke modello BeginInvoke, sarà asincrono chiamate entro il primo delegato onorare l'inizio/fine anche?

Esempio:

public void dele1(string message) { 
    Console.Write(message); 
    delegate2 del2 = new delegate2; 
    del2(); 
    Console.Write("End of Delegate 2"); 
} 

public void dele2() { 
    // Long Processing 
    Console.Write("Delegate 2"); 
} 

public delegate void delegate1(String message); 
public delegate void delegate2(); 

delegate1 del1 = new delegate1(dele1); 
del1("Delegate 1").BeginInvoke; 
del1().EndInvoke; 
Console.Write("End of Delegate 1"); 

// Output previsto (Fine Invoke attende fino Delegato 2 è terminata):

Delegate 1 
End of Delegate 2 
Delegate 2 
End of Delegate 1 

// O (Fine Invoke aspetta solo delegato 1 per finire, ma non qualsiasi chiamata interna delegata):

Delegate 1 
End of Delegate 2 
End of Delegate 1 
Delegate 2 

Termina invoke attendere fino a quando il secondo delegato termina l'elaborazione o? O dovrò usare i pattern di invocazione su tutte le chiamate delegate?

+0

Non sono sicuro della sintassi per l'esempio modificato. Puoi chiarire di più cosa sta succedendo con del1? – strager

risposta

7

È possibile utilizzare monitor, semafori o persino attivare l'attesa fino al completamento delle chiamate del metodo asincrono.

Ma puoi ottenere anche questo gratuitamente. Se si chiama EndInvoke() su un delegato che era stato precedentemente avviato con BeginInvoke(), si blocca fino al completamento del lavoro asincrono.

Non sono sicuro se questo aiuta perché è necessario utilizzare questo modello asincrono. Se lo fa, ottieni l'esecuzione asincrona (e la conversione da Asincrono alle chiamate sincrone) gratuitamente.

Scopri Calling Synchronous Methods Asynchronously on MSDN per ulteriori informazioni su questo modello.

Spero che questo aiuti!

+0

Sembra proprio quello di cui ho bisogno! Grazie compagno. –

2

Mi sembra che tu voglia una coda ... se c'è una richiesta in corso, aggiungi alla coda (con un Monitor); al termine di una chiamata, controlla la coda ...

0

Hai alcune opzioni. Ecco un paio di idee:

Prima di tutto, è possibile utilizzare un ManualResetEvent per "bloccare" il thread principale fino al completamento delle operazioni asincrone. L'idea è di fare in modo che il thread principale chiami la funzione, quindi fai event.WaitOne() e la funzione è responsabile dell'impostazione dell'evento.

In alternativa, è possibile provare a utilizzare qualcosa come un Semaphore. È progettato per aiutare in queste situazioni, dal momento che è possibile limitarlo a 10 eventi simulatanei senza preoccuparsi di provare a gestirlo da soli. Questo probabilmente sarebbe il mio approccio preferito.

0

Ci sono due ragioni per usare le discussioni:

  • di approfittare delle risorse della CPU, in modo da velocizzare le cose
  • a scrivere diversi macchine a stati simultanei metodi come semplici, che li rende più leggibile

Lo svantaggio dell'utilizzo di fili è:

  • è reall y difficile

Se il collo di bottiglia è un'attesa di 3 minuti su una macchina remota, può valere la pena notare che più thread non ti garantiranno alcun vantaggio in termini di prestazioni. Potresti scrivere l'intero thiing a thread singolo, mantenendo un set di fino a dieci oggetti "state machine" e spostandoli attraverso le fasi della creazione dell'utente, il tutto da un thread, con poca differenza in tutte le prestazioni.

Quindi quello che stai cercando è un layout di codice leggibile, rendendo l'operazione di creazione dell'utente in una sequenza di chiamate che leggono come un singolo metodo (che sarebbero se le avessi eseguite come thread).

Un altro modo per ottenere ciò avviene tramite un metodo iteratore, ovvero contenente le istruzioni yield return, che offre anche i vantaggi della leggibilità a metodo singolo. Devo avere linked to this three times nell'ultima settimana! È l'articolo di Jeffrey Richter su AsyncEnumerator.

2

IAsyncResult ha una proprietà AsyncWaitHandle che fornisce un handle di attesa (se disponibile) che verrà segnalato al termine dell'operazione.

Quindi è possibile utilizzare WaitHandle.WaitAll (o .WaitAny) per eseguire attese non in rotazione su più maniglie contemporaneamente.

0

Sono un po 'incerto su come si utilizza TransactionScope (in modo che possa essere fuori base), ma è possibile creare DependentTransactions che può essere distribuito ai thread di lavoro. La transazione di livello superiore non verrà confermata fino a quando tutte le transazioni DependentTransactions non saranno state correttamente eseguite e viceversa con il rollback. Ho trovato questo come un modo semplice per fare tutto o niente attraverso i thread purché l'operazione che stai invocando incapsula la funzionalità di commit/rollback (IEnlistmentNotification o simili).

+0

Hai ragione, e se non fossimo limitati alle 10 connessioni max per il processo finale, questo andrebbe bene. Sfortunatamente, siamo limitati a 10 (in realtà ci è consentito 2), quindi è più semplice eseguirli tutti in sequenza anziché tenere una coda. –

+0

Connessioni concorrenti limitate ... sempre un requisito divertente;) – jsw