C #: Bloccare una chiamata di funzione finché la condizione è riunito
Domanda
Sto sviluppando un'applicazione C # WinForms, parte della domanda sarà il caricamento di file su un server web usando AsyncUpload (ad usarlo, a causa della necessità di utilizzare un callback porgress), Nel programma C #
ho ottenuto un semplice ciclo for che chiama la funzione Caricamento
for(int i=0;i < 10 ; i++)
{
Uploadfun();
}
E il divertimento non qualche magia:
Uploadfun()
{
// Logic comes here
// webClient.UploadFileAsync runs a 2nd thread to perform upload ..
webClient.UploadFileAsync(uri, "PUT", fileNameOnHD);
}
E un callback che viene chiamata quando il caricamento asincrono è fatto
Upload_Completed_callback()
{
//Callback event
}
Modifica
La sequenza logica:
- Divertimento viene chiamato (da anello)
- logica divertimento è eseguito e fatto ..
- Ritorna alla ciclo for
- callback verrà chiamata casualmente, quando UploadFileAsync (che esegue una logica in un altro thread) finirà
Il problema è al 3 ° punto, quando l'esecuzione si sposta di nuovo al ciclo for, ho bisogno di bloccare il ciclo di continuare fino a quando il callback vengono chiamati.
Soluzione
Quindi, se ho capito bene, si desidera chiamare UploadFileAsync
quindi bloccare fino a quando la chiamata asincrona ha colpito il callback. Se è così, mi piacerebbe utilizzare AutoResetEvent
cioè
private readonly AutoResetEvent _signal = new AutoResetEvent(false);
fun()
{
// Logic comes here
// runs a 2nd thread to perform upload .. calling "callback()" when done
webClient.UploadFileAsync(uri, "PUT", fileNameOnHD);
_signal.WaitOne(); // wait for the async call to complete and hit the callback
}
callback()
{
//Callback event
_signal.Set(); // signal that the async upload completed
}
Uso AutoResetEvent
significa che lo stato viene resettato automaticamente dopo Set
è stata chiamata e un filo attesa riceve il segnale tramite WaitOne
Altri suggerimenti
In C # i metodi blocco di default, quindi non dovrebbe essere necessario fare nulla. Sto assumendo che per qualche motivo si sta chiamando un metodo non-blocking che inizia uno sfondo compito / thread / qualunque cosa e ti dà un callback quando è fatto. Si desidera chiamare questo metodo asynchonous in modo sincrono.
È possibile chiamare fun
dall'interno della richiamata. Qualcosa in questo senso (pseudo-codice):
int n;
callFunTenTimes()
{
n = 0;
fun(n);
}
callback()
{
++n;
if (n < 10)
fun(n);
else
print("done");
}
Questo è simile a passando stile .
Un vantaggio di questo metodo è che si può anche fare il vostro metodo asincrono senza l'aggiunta di fili aggiuntivi, serrature, o la logica in più - voi appena fornisce una funzione di callback che il vostro cliente può sottoscrivere. Funziona bene in un ambiente event-driven.
Zebrabox non ce l'ha proprio con un WaitHandle. Mentre la soluzione di Giulietta funziona, il filo eseguendo lo spin-wait consumerà una quantità significativa di processore in proporzione alla WaitHandle, che sarebbe essenzialmente seduto inattivo.
Il problema è qui:
for(int i=0;i < 10 ; i++)
{
fun(); <-- if we block until this function finishes here, we stop the UI thread
}
Quello che stai facendo è sequenziale. E se non si può permettere di bloccare il thread UI, spostare l'anello fuori dal thread dell'interfaccia utente:
volatile downloadComplete;
void DownloadUpdates()
{
ThreadPool.QueueUserWorkItem(state =>
for(int i = 0; i < 10; i++)
{
downloadComplete = false;
webClient.UploadFileAsync(uri, "PUT", fileNameOnHD);
while(!downloadComplete) { Thread.Sleep(1); }
});
}
Upload_Completed_callback()
{
downloadComplete = true;
}
Ora è possibile bloccare l'esecuzione del ciclo, senza arrestare il tuo thread dell'interfaccia utente, e si ottiene anche il vantaggio di indicatori di avanzamento dalla classe WebClient.