2015-07-01 12 views
9

voglio che il mio programma da attendere dopo che sotto la lineaCome attendere filo per completare senza bloccare UI

frmProgressBarObj = PullMSI.ExtractByMSIName("products.txt", false); 

come sopra metodo sta chiamando internamente filo attraverso StartProcessWithProgress() metodo. Voglio che il thread sia completato prima che // codice logic -2 line venga eseguito. Allo stesso tempo, non dovrebbe interrompere l'aggiornamento dell'interfaccia utente fatto da frmProgressBar.UpdateProgress(). Come faccio a fare questo?

namespace NS1 
{ 
    public partial class frmMain : Form 
    {     
     private void button1_Click(object sender, EventArgs e) 
     { 
      frmProgressBar frmProgressBarObj = PullMSI.ExtractByMSIName("products.txt", false); 
      //code logic - 2 
      MessageBox.Show("This is executing immediately. 
          I want to wait until above thread is complete"); 
     } 
    } 

    public partial class frmProgressBar : Form 
    { 

     public void UpdateProgress(String strTextToDisplayOnProgress) 
     { 
      progressBar1.BeginInvoke(
        new Action(() => 
        { 
         progressBar1.Value++; 
         lblFileName.Text = strTextToDisplayOnProgress; 
         if (progressBar1.Value == progressBar1.Maximum) 
         { 
          this.Hide(); 
         } 
        })); 
     } 

     public delegate void DelProgress(); 

     public void StartProcessWithProgress(DelProgress delMethodCode, int maxCount) 
     { 
      InitializeProgress(maxCount); 
      Thread backgroundThread = new Thread(new ThreadStart(delMethodCode)); 
      backgroundThread.Start(); 
     } 
    } 

    public static class PullMSI 
    { 
     public static frmProgressBar ExtractByMSIName(String strProductFilePath, bool reNameMSI) 
     { 
      frmProgressBar frmProgressBar = new frmProgressBar(); 

      frmProgressBar.StartProcessWithProgress(() => 
      { 
       //StreamRader sr declaration and other code 

       while (!sr.EndOfStream) 
       { 
        //logic here 
        frmProgressBar.UpdateProgress("Copying sr.msiname"); 
       } 
      }, 2); 

      return frmProgressBar; 
     } 
    } 
} 
+0

UI? Questa è un'applicazione per console. –

+1

Il mio male. Scusa, per semplificare il codice, ho appena messo tutte le classi nell'applicazione console in modo da poter pubblicare facilmente qui. Ma la sua applicazione di Windows forma e processo iniziano sul tasto clic. – Akie

+1

È possibile utilizzare uno di questi: 1) TPL con la continuazione dell'attività 2) Ripristina eventi (ManualResetEventSlim/AutoResetEventSlim) 3) Utilizzando altri meccanismi come Semaphore (scoraggiarti molto di farlo) 4) async/attendi se stai eseguendo. Net 4.5+. 5) Produttore/consumatore (overkill per il tuo caso d'uso). Fondamentalmente ci sono una varietà di modi in cui questo è possibile. Fai una lettura e scegli quella che ti piace di più. – kha

risposta

13

Sono molto sorpreso che tu non abbia mai lavorato con nessuno di questi prima, ma mi raccomando di leggere sul threading in C# poiché è di fondamentale importanza comprendere le complessità e imparare la lingua.

Qui di seguito sono tre modi diversi si possono ottenere ciò che si vuole:

1. Utilizzo di eventi di ripristino (ulteriore lettura: https://msdn.microsoft.com/en-us/library/system.threading.manualreseteventslim(v=vs.110).aspx). Se la versione C# non ha il ManualResetEventSlim, sostituirlo con ManualResetEvent e cambiare Wait() con WaitOne()

class LockingWithResetEvents 
{ 
    private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); 

    public void Test() 
    { 
     MethodUsingResetEvents(); 
    } 

    private void MethodUsingResetEvents() 
    { 
     ThreadPool.QueueUserWorkItem(_ => DoSomethingLong()); 
     ThreadPool.QueueUserWorkItem(_ => ShowMessageBox()); 
    } 

    private void DoSomethingLong() 
    { 
     Console.WriteLine("Doing somthing."); 
     Thread.Sleep(1000); 
     _resetEvent.Set(); 
    } 

    private void ShowMessageBox() 
    { 
     _resetEvent.WaitOne(); 
     Console.WriteLine("Hello world."); 
    } 
} 

2) Uso di Task Parallel Library (TPL). Ulteriore lettura: https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx

class LockingWithTPL 
{ 
    public void Test() 
    { 
     Task.Factory.StartNew(DoSomethingLong).ContinueWith(result => ShowMessageBox()); 
    } 

    private void DoSomethingLong() 
    { 
     Console.WriteLine("Doing somthing."); 
     Thread.Sleep(1000); 
    } 

    private void ShowMessageBox() 
    { 
     Console.WriteLine("Hello world."); 
    } 
} 

3) Utilizzo di Async/attendono. Per ulteriori approfondimenti:https://msdn.microsoft.com/en-us/library/hh191443.aspx

class LockingWithAwait 
{ 
    public void Test() 
    { 
     DoSomething(); 
    } 

    private async void DoSomething() 
    { 
     await Task.Run(() => DoSomethingLong()); 
     ShowMessageBox(); 
    } 

    private async void DoSomethingLong() 
    { 
     Console.WriteLine("Doing somthing."); 
     Thread.Sleep(10000); 
    } 

    private void ShowMessageBox() 
    { 
     Console.WriteLine("Hello world."); 
    } 
} 

anche bene sapere: Mutex (https://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx), semaforo (https://msdn.microsoft.com/en-us/library/system.threading.semaphore(v=vs.110).aspx), Lock (https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx), SemaphoreSlim (https://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim(v=vs.110).aspx), Monitor (https://msdn.microsoft.com/en-us/library/system.threading.monitor(v=vs.110).aspx) e prese (https://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx).

+0

Non è necessario il metodo 'Test' essere dichiarato 'Async' anche? Ho pensato che tutti i metodi che chiamano i metodi Async dovevano essere anche Async. Async/Await è ancora un po 'nuovo per me, quindi potrei avere sbagliato. –

+0

@BradleyUffner Non è necessario. Non sto aspettando nulla. Se non lo stai aspettando, non ha bisogno della parola chiave 'async' e lo tratterà invece come un metodo semplice con tipo di ritorno vuoto. Questo tuttavia non sarebbe compilato: 'attendi DoSomething();'. – kha

+0

NO, no, nessuna attesa in gestori di eventi UI! –

2

Se stai usando .NET 4.0 (con VS2012) o superiore, è possibile farlo abbastanza facilmente con l'Task Parallel Library e async-await:

private async void button1_Click(object sender, EventArgs e) 
{ 
    frmProgressBar frmProgressBarObj = await Task.Run(() => 
         PullMSI.ExtractByMSIName("products.txt", false)); 

    MessageBox.Show(string.Format("Returned {0}", frmProgressBarObj.ToString()); 
} 

Per .NET 4, è necessario aggiungere Microsoft.Bcl.Async.

+2

Vale la pena notare che questa è un'opzione solo se l'autore sta lavorando con .NET 4.5 –

+0

@TomC. NET 4.0 supporta anche 'async-await' con' Microsoft.Bcl .Async'. –

+1

@YuvalItzchakov Per alcuni valori di "supporto" – Aron