2010-02-06 11 views
12

Sto sviluppando un'applicazione C# Winforms, una parte dell'applicazione caricherà i file su un server web utilizzando AsyncUpload (utilizzandolo, a causa della necessità di utilizzare un callback del porge), nel C# programmaC#: blocco di una chiamata di funzione fino a condizione soddisfatta

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 chiamato w uando il caricamento asincrono è fatto

Upload_Completed_callback() 
{ 
    //Callback event 
} 

Modifica

La sequenza logica:

  1. Fun viene chiamato (da anello)
  2. logica divertimento viene eseguito e fatto ..
  3. Torna al ciclo for
  4. Il callback verrà chiamato eventuall y, quando UploadFileAsync (che esegue un po 'di logica in un altro thread) terminerà

Il problema si trova sul terzo punto, quando l'esecuzione ritorna al ciclo for, ho bisogno di bloccare il ciclo da continuare fino al richiamata chiamata.

+3

Avete accesso all'implementazione 'divertente'? Probabilmente dovresti considerare di fornire un'interfaccia sincrona su cui è implementata l'API asincrona. –

+0

Nel modo in cui lo descrivi, il tuo codice sembra sequenziale e con un singolo thread, il che significa che fino a quando non viene chiamata la funzione 'callback', il ciclo non continuerà. Per favore correggimi se ho torto fornendo una spiegazione più dettagliata. –

+0

@Madi: quindi stai facendo questo all'indietro. Dovresti utilizzare la versione sincrona di "UploadFile" al centro della logica e utilizzare un'API asincrona su di essa se ne hai bisogno. –

risposta

20

Quindi, se ho capito correttamente, si desidera chiamare UploadFileAsync quindi bloccare fino a quando la chiamata asincrona ha raggiunto la richiamata. 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 
} 

Utilizzando AutoResetEvent significa che lo stato viene resettato automaticamente dopo Set è stato chiamato e un thread in attesa riceve il segnale tramite WaitOne

+0

quando si utilizza Waitone() dopo uploadAsync, il programma si blocca e il callback non viene chiamato. –

+0

okie .. ho risolto il problema dell'arresto, chiamato fun() in un thread separato, "ovviamente" chiamandolo nel thread principale sta causando il problema!, @Juliet Kinda ha aiutato a sottolineare il problema .. grazie a entrambi =) –

4

Nel blocco di metodi C# per impostazione predefinita, non è necessario eseguire alcuna operazione. Suppongo che per qualche ragione tu stia chiamando un metodo non bloccante che avvia un'attività in background/thread/quant'altro e ti dà una richiamata quando ha finito. Vuoi chiamare questo metodo asincrono in modo sincrono.

È possibile chiamare fun dall'interno del callback. 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 continuation passing style.

Un vantaggio di questo metodo è che è possibile anche rendere il metodo asincrono senza aggiungere thread aggiuntivi, blocchi o logica aggiuntiva: è sufficiente fornire una funzione di callback a cui il client può iscriversi. Funziona bene in un ambiente guidato dagli eventi.

+0

Questo è divertente. Ho pensato al mio problema in questo modo: avere la funzione di callback fa la 'carne' di ciò che il ciclo usava fare. Questa soluzione sembra mantenere il tempo di attesa al minimo. – Joe

+0

ho bisogno della versione asincrona perché fornisce un callback "progresso" che è un requisito fondamentale. –

+0

@Madi D: Se il requisito è che il caricamento debba comportarsi in modo asincrono, perché non rendere la funzione anche asincrona, con una richiamata? Puoi spiegare di più su cosa stai usando per questo? È un'app WinForms, ASP.NET, ecc.? –

1

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 UI:

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 progresso dalla classe webclient.

+0

Non usare una variabile booleana per questo, il compilatore può ottimizzarlo (il che farebbe sì che il comportamento di non continuare mai che l'OP accenna). zebrabox ha mostrato il modo giusto per farlo, con un oggetto evento attendibile. –

+1

@ Ben: apprezzo i tuoi commenti, ma penso che siano fuorvianti. Innanzitutto, il compilatore non ottimizzerà il valore bool a patto che sia usato o assegnato da qualche parte (e presumibilmente lo è). Secondo, spin-waiting su un bool e resetEvents sono modi validi per bloccare un thread, entrambi sono il "modo giusto per farlo", né sono "sbagliati", scegliere l'uno o l'altro dipende dai gusti del programmatore. – Juliet

+0

Grazie mille per la risposta davvero utile, finita per combinare il threading dalla soluzione con la soluzione resetEvent di @ zebrabox, e ora funziona .. =) –

3

Zebrabox funziona correttamente con WaitHandle. Mentre la soluzione di Juliet funziona, il thread che esegue lo spin-wait consumerà una quantità significativa di processore in proporzione al WaitHandle, che sarebbe sostanzialmente inattivo.

+0

+1 fa luce su entrambe le risposte migliori (per me) .. –

+0

Non consumerà molto processore, ma potrebbe impedire al sistema di mettere la CPU in uno stato di inattività più profondo e quindi abbassare la batteria più velocemente su un il computer portatile. –

Problemi correlati