2012-03-08 17 views
5

Ho una piccola applicazione che deve testare stringhe di connessione SQL per un numero di connessioni (ognuna eseguita una alla volta). Per fare questo ho impostato il ConnectionTimeout = 5 temporaneamente per evitare una lunga attesa se la connessione non è valida e ConnectionTimeout = 0 (attendere per sempre), ad esempio.TPL Come eseguire una "richiamata"

Per evitare l'impiccagione UI mentre tentiamo di Open() una cattiva connessione (anche con ConnectionTimeout = 5 l'attesa per il SqlException può essere fino a venti secondi), voglio eseguire il test su un thread separato utilizzando Task Parallel Library (TPL). Così ho spin-off il mio nuovo thread come:

Task<bool> asyncTestConn = Task.Factory.StartNew<bool> 
    (() => TestConnection(conn, bShowErrMsg)); 
return asyncTestConn.Result; 

Il problema è che questo è ancora chiudendo l'interfaccia utente (chiaramente), in quanto è in attesa per il risultato prima di ritornare al chiamante. Come posso consentire al codice di restituire il controllo all'interfaccia utente (liberando la GUI) mentre ottengo il risultato finale dallo Task asincrono?

Inoltre, dall'interno di uno Task posso legittimamente fare MessageBox.Show("Some message")? Questo non funziona per BackgroundWorkers e questo pool di thread è un thread in background per impostazione predefinita; eppure non sembra essere un problema. Grazie per il tuo tempo.

risposta

4

Hai ragione, questo è dove l'attesa succede:

return asyncTestConn.Result; 

Si potrebbe semplicemente creare il codice della finitura-up nella coda della TestConnection() o utilizzare una continuazione:

// untested 
//Task<bool> asyncTestConn = Task.Factory.Create<bool> (
Task<bool> asyncTestConn = new Task<bool> (
    () => TestConnection(conn, bShowErrMsg)); 
asyncTestConn.ContinueWith(MyFinishCode); 
asyncTestConn.Start() 

posso legittimamente fare MessageBox.Show("Some message")?

In realtà Sì, MessageBox è thread-safe. Dovrebbe essere possibile anche da una Bgw.

Ma si sta prolungando molto la vita del compito, non è una buona idea.

+0

Grazie mille per la risposta. L'idea è di tornare al controllo dell'interfaccia utente appena possibile, come fa/può ottenere quanto sopra? Se sto chiamando il codice precedente da un metodo chiamato 'ParrTestConn (SqlConnection conn, string bShowErrMsg)' allora non posso dire 'asyncTestConn.ContinueWith (ParrTestConn (conn, bShowErrMsg))' ... Oppure posso? – MoonKnight

+0

@Killer Si può semplicemente tornare dopo Start(). La GUI dovrebbe rimanere reattiva quando si usa questo. –

+0

Non ho il metodo 'Task.Factory.Create ' disponibile? – MoonKnight

5

Per il TPL, ContinueWith è esattamente quello che vuoi. Espansione sulla risposta di Henk:

var asyncTestConn = Task.Factory.StartNew(() => TestConnection(conn, bShowErrMsg)); 
// Henk's "MyFinishCode" takes a parameter representing the completed 
// or faulted connection-testing task. 
// Anything that depended on your "return asyncTestConn.Result;" statement 
// needs to move into the callback method. 
asyncTestConn.ContinueWith(task => 
    { 
     switch (task.Status) 
     { 
      // Handle any exceptions to prevent UnobservedTaskException. 
      case TaskStatus.Faulted: /* Error-handling logic */ break; 
      case TaskStatus.RanToCompletion: /* Use task.Result here */ break; 
     } 
    }, 
    // Using this TaskScheduler schedules the callback to run on the UI thread. 
    TaskScheduler.FromCurrentSynchronizationContext()); 
+0

Davvero utile. Grazie. La combinazione delle due risposte è assolutamente senza parole. – MoonKnight