2011-09-23 24 views
7

Ho creato una semplice applicazione WPF e aggiunto un pulsante alla finestra predefinita. Quando faccio clic sul pulsante, viene chiamato un metodo di simulazione a lungo simulato (simulato utilizzando Thread.Sleep (15000). Sto tentando di eseguire il pulsante in modo asincrono tuttavia nonostante i seguenti esempi online, il pulsante e l'intera finestra si bloccano non appena clicca e rimane tale fino alla Thread.Sleep (...) finisceBlocco filettatura UI

Tutte le idee perché questo sta accadendo

Ecco il codice:.?

private void button1_Click(object sender, RoutedEventArgs e) 
{ 
    DoSomeAsyncWork(); 
} 

private void DoSomeAsyncWork() 
{ 
    System.Windows.Threading.Dispatcher.Run(); 
    Thread thread = new System.Threading.Thread(
     new System.Threading.ThreadStart(
      delegate() 
      { 
       Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => Thread.Sleep(15000))); 
      } 
     )); 
    thread.Start(); 
} 

risposta

12

Si sta reinserendo la lunga operazione nel thread dell'interfaccia utente. Consentitemi di commentare il tuo esempio:

Thread thread = new System.Threading.Thread( 
    new System.Threading.ThreadStart( 
     delegate() { 
      // here we are in the background thread 

      Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, 
       new Action(() => { 
        // here we are back in the UI thread 
        Thread.Sleep(15000); 
       })); 
     } 
    )); 

Quindi, è necessario modificare il vostro esempio come questo:

Thread thread = new System.Threading.Thread( 
    new System.Threading.ThreadStart( 
     delegate() { 
      // here we are in the background thread 

      Thread.Sleep(15000); // <-- do the long operation here 

      Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, 
       new Action(() => { 
        // here we are back in the UI thread 

        // do stuff here that needs to update the UI after the operation finished 
       })); 
     } 
    )); 

Come altri hanno detto, è più facile utilizzare la classe BackgroundWorker. Ecco un esempio:

private void DoSomeAsyncWork() 
{ 
    BackgroundWorker bw = new BackgroundWorker(); 

    bw.DoWork += (sender, args) => { 
     // do your lengthy stuff here -- this will happen in a separate thread 
     Thread.Sleep(15000); 
    } 

    bw.RunWorkerCompleted += (sender, args) => { 
     if (args.Error != null) // if an exception occurred during DoWork, 
      MessageBox.Show(args.Error.ToString()); // do your error handling here 

     // do any UI stuff after the long operation here 
     ... 
    } 

    bw.RunWorkerAsync(); // start the background worker 
} 
3

Utilizzando BeginInvoke in realtà si sta eseguendo il codice a thread dell'interfaccia utente.

È necessario utilizzarlo solo quando si aggiorna l'interfaccia utente dal thread di lavoro in background. Se hai semplicemente:

Thread thread = new System.Threading.Thread(
    new System.Threading.ThreadStart(
     delegate() 
     { 
      Thread.Sleep(15000); 
     } 
    )); 

Penso che funzionerà.

Tuttavia, non si sta generando un evento "Work Completed" in modo che non si abbia modo di sapere quando (o effettivamente se) il thread ha terminato. Guarda nello BackgroundWorker class. Questo fa molto per te. Hai solo bisogno di collegare il tuo codice al metodo DoWork.

+0

sto effettivamente cercando di eseguire del codice che richiede il proprio filo e quando ho posto questa riga di codice direttamente nel primo delegato (cioè dove si ha la Thread.Sleep (15000)) I ricevi un messaggio che dice che il thread non può accedere agli oggetti. Questo è il messaggio: "Il thread chiamante non può accedere a questo oggetto perché lo possiede un thread diverso." Apparentemente, è un problema comune, ma come faccio a lottare per aggirare questo senza andare lungo il percorso dell'utilizzo di un lavoratore in background. Quindi, quando ho aggiunto la seconda chiamata BeginInvoke nidificata, l'oggetto viene trovato ma l'interfaccia utente è bloccata. – fin

+0

Quindi, suppongo, quello che sto cercando è un metodo per mettere la mia logica che apparentemente richiede l'accesso al thread dell'interfaccia utente su un nuovo thread in background. Riassumendo, concedi l'accesso agli oggetti dell'interfaccia utente, ma esegui su un thread separato in background? – fin

+0

@Finbar: non è possibile "concedere l'accesso". Le operazioni dell'interfaccia utente * devono * essere eseguite nel thread dell'interfaccia utente. Ciò che si può fare, tuttavia, è dividere le operazioni dell'interfaccia utente in piccoli bit, inserire il grande loop nel thread in background e chiamare 'Dispatcher.BeginInvoke (... short UI operation ...)' all'interno del ciclo. Ciò fa sì che l'interfaccia utente venga bloccata a breve molte volte anziché una volta tanto. – Heinzi