2015-07-01 105 views
9

完成我希望我的程序等待下面一行如何等待線程不會阻塞UI

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

如上述方法在內部調用通過StartProcessWithProgress線程()方法後。我希望線程在//代碼邏輯-2行被執行之前完成。同時,它不應該停止由frmProgressBar.UpdateProgress()完成UI更新。我該怎麼做呢?

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?這是一個控制檯應用程序。 –

+1

我的不好。對不起,爲了簡化代碼,我只是將所有類放在控制檯應用程序中,以便我可以輕鬆地在此處發佈。但它的Windows窗體應用程序和進程開始點擊按鈕。 – Akie

+1

您可以使用以下兩種方法之一:1)TPL與任務繼續2)重置事件(ManualResetEventSlim/AutoResetEventSlim)3)使用其他機制,如信號量(高度阻止你這樣做)4)異步/等待,如果你正在運行。淨4.5+。 5)生產者/消費者(針對您的使用案例的過度使用)。基本上可以有多種方式。做一個閱讀這些,並選擇一個你最喜歡的。 – kha

回答

13

我很驚訝你之前沒有使用過任何這些工具,但是我真的會推薦閱讀關於C#中的線程,因爲了解錯綜複雜和學習語言是非常重要的。

下面是三種不同的方法,你可以達到你想要的東西:

1.使用復位事件(延伸閱讀:https://msdn.microsoft.com/en-us/library/system.threading.manualreseteventslim(v=vs.110).aspx)。如果你的C#版本不具備ManualResetEventSlim,與ManualResetEvent替換和更改Wait()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)使用任務並行庫(TPL)。進一步閱讀: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)使用異步/等待。延伸閱讀: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."); 
    } 
} 

還不錯知道:互斥(https://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx),信號量(https://msdn.microsoft.com/en-us/library/system.threading.semaphore(v=vs.110).aspx),鎖(https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx),SemaphoreSlim(https://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim(v=vs.110).aspx),監視器(https://msdn.microsoft.com/en-us/library/system.threading.monitor(v=vs.110).aspx)和互鎖(https://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx)。

+0

您的「測試」方法是否需要被宣佈爲'Async'呢?我認爲所有調用Async方法的方法都必須是Async。異步/等待對我來說還是一個新的東西,所以我可能會犯這個錯誤。 –

+0

@BradleyUffner不必。我不是在等待任何事情。如果你不等待它,它不需要'async'關鍵字,而是將它作爲一個帶有void返回類型的簡單方法處理。但是這不會編譯:'await DoSomething();'。 – kha

+0

不,不,不等待UI事件處理程序! –

2

如果你使用.NET 4.0(與VS2012)或以上,你可以做到這一點很容易與Task Parallel Libraryasync-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()); 
} 

對於.NET 4,你需要添加Microsoft.Bcl.Async

+2

值得注意的是,只有當作者使用.NET 4.5 –

+0

@TomC時,這是一個選項,.NET 4.0也支持'async-await'和'Microsoft.Bcl .Async'。 –

+1

@YuvalItzchakov對於「支持」的某些值 – Aron