2015-07-01 7 views
9

Ich möchte mein Programm nachWie für Thread warten, ohne zu beenden UI blockiert

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

unter der Schlange warten, wie oben Verfahren intern Faden durch StartProcessWithProgress() -Methode aufrufen. Ich möchte, dass dieser Thread abgeschlossen wird, bevor // Code-Logik -2 Zeile ausgeführt wird. Zur gleichen Zeit sollte die UI-Aktualisierung von frmProgressBar.UpdateProgress() nicht gestoppt werden. Wie mache ich das?

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? Dies ist eine Konsolenanwendung. –

+1

Mein schlechtes. Sorry, Um den Code zu vereinfachen, habe ich einfach alle Klassen in die Konsolenanwendung gelegt, damit ich sie hier einfach posten kann. Aber seine Windows-Formular-Anwendung und Prozess starten auf Knopfdruck. – Akie

+1

Sie können eine der folgenden Möglichkeiten nutzen: 1) TPL mit Taskfortsetzung 2) Ereignisse zurücksetzen (ManualResetEventSlim/AutoResetEventSlim) 3) Andere Mechanismen wie Semaphore verwenden (davon abraten, dies zu tun) 4) async/await, wenn Sie gerade laufen. Netto 4.5+. 5) Produzent/Verbraucher (Overkill für Ihren Anwendungsfall). Grundsätzlich gibt es eine Vielzahl von Möglichkeiten, dies zu erreichen. Lesen Sie diese und lesen Sie eine, die Ihnen am besten gefällt. – kha

Antwort

13

Ich bin sehr überrascht, dass Sie nicht mit irgendwelchen gearbeitet haben, aber ich würde in C# über Threading, da es grundlegend wichtig ist, um die Feinheiten zu verstehen und zu lernen, die Sprache wirklich zu lesen empfehlen.

Im Folgenden sind drei verschiedene Möglichkeiten, die Sie erreichen können, was Sie wollen:

1. Reset-Ereignisse Mit (Lesen Sie auch: https://msdn.microsoft.com/en-us/library/system.threading.manualreseteventslim(v=vs.110).aspx). Wenn Ihr C# Version entspricht nicht der ManualResetEventSlim hat, ersetzen Sie es durch ManualResetEvent und ändern Wait() mit 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) mit dem Task Parallel Library (TPL). Weiterführende Literatur: 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) Mit Async/Await. Weiterführende Literatur: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."); 
    } 
} 

Auch gut zu wissen: Mutex (https://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx), Semaphore (https://msdn.microsoft.com/en-us/library/system.threading.semaphore(v=vs.110).aspx), Schloss (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) und verzahnt (https://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx).

+0

Hat Ihre 'Test'-Methode nicht auch als "Async" deklariert werden? Ich dachte, dass alle Methoden, die Async-Methoden aufrufen, auch Async sein müssen. Async/Await ist mir immer noch ein wenig neu, also kann ich das falsch verstehen. –

+0

@BradleyUffner Muss nicht. Ich erwarte nichts. Wenn Sie nicht darauf warten, benötigt es das Schlüsselwort async nicht und behandelt es stattdessen als einfache Methode mit void return type. Dies würde jedoch nicht compilieren: 'awar DoSomething();'. – kha

+0

Nein, nein, kein Warten in UI Event-Handlern! –

2

Wenn Sie .NET 4.0 (mit VS2012) oder höher verwenden, können Sie dies ganz einfach mit dem Task Parallel Library und 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()); 
} 

Für .NET 4, die Sie benötigen hinzufügen Microsoft.Bcl.Async.

+2

Es ist erwähnenswert, dass dies nur eine Option ist, wenn der Autor mit .NET 4.5 –

+0

@TomC arbeitet.NET 4.0 unterstützt auch 'async-await' mit' Microsoft.Bcl .Async'. –

+1

@YuvalItzchakov Für einige Werte von "support" – Aron

Verwandte Themen