2017-10-16 77 views
0

感謝您閱讀本主題。C#(WPF)異步線程與界面到GUI

對於一個新的WPF應用程序(用C#構建)我有一個關於設計的問題。 過去幾天我已經閱讀了很多關於C#中的異步編程(基於.NET 4.5)。

我們想要做的是:創建一個新的異步線程,它執行獨立的後臺任務。當這個線程有可用數據時:然後將這些數據發送到主程序(通過公共接口)。所以,線程將在主程序中設置數據並立即再次返回到線程。數據更改後,主程序將引發一個事件(INotifyPropertyChanged)。

什麼是創建這個異步線程的最佳方式?或者至少,設計此功能的最佳方式是什麼?

目前我已經構建了一個創建線程的應用程序。 這不目前工作異步:

public MainWindow() 
    { 
     InitializeComponent(); 

     InitGuiInterface(this); 

     //Create thread 
     new OuterLabel_Thread(this); 
    } 

和類 「OuterLabel_Thread.cs」 這裏下面:提前

public class OuterLabel_Thread 
{ 
    private MainWindow context = null; 
    private bool exit = false; 
    private int count = 0; 

    public OuterLabel_Thread(MainWindow context) 
    { 
     this.context = context; 

     Console.WriteLine("Running sample thread"); 
     Thread thread = new Thread(delegate() 
     { 
      Console.WriteLine("Sample thread started"); 

      //start new task 
      //run(); 
      Task.Factory.StartNew(run); 
     }); 
     thread.Start(); 
    } 

    public void Exit() 
    { 
     exit = true; 
    } 

    private void run() 
    { 
     while (!exit) 
     { 
      DateTime Time1 = DateTime.Now; 

      if (context != null && context.GuiInterface != null) 
      { 
       //context.GuiInterface.UpdateThreadCount(count, "label_code_content"); 
      } 
      Console.WriteLine("Background thread count = " + count); 

      count++; 
      if (count > 1000) 
      { 
       exit = true; 
      } 
      //Console.WriteLine((DateTime.Now - Time1).TotalMilliseconds.ToString()); 
      Thread.Sleep(10); 
     } 
    } 
} 

非常感謝! 親切的問候,

Rein。

+0

爲什麼不只是使用異步等待?這將使管理UI線程和避免交叉線程錯誤變得容易。你可以避免重複性問題,而不是試圖管理你自己的線程。 – CodeMonkey

+0

我沒有太多的使用異步和等待的經驗。我知道這些說明是幹什麼的。 您的建議仍然基於線程的使用情況嗎?在這種情況下,我該如何調用線程? – Rein92

+0

您是否正在尋找一種可以連續提供數據的服務,或者只需點擊一個按鈕即可開始一項長時間運行的任務? – CodeMonkey

回答

1

最好的辦法是使用異步+等待和任務。

private async void LaunchButton_OnClick(object sender, RoutedEventArgs e) 
    { 
     resultLabel.Content = "Task running"; 
     resultLabel.Content = await SomeLongRunningTaskAsync(); 
    } 

    private Task<string> SomeLongRunningTaskAsync() 
    { 
     return Task.Run(
      () => 
      { 
       // Put your background work in here. with Task.Run it's not going to run on UI 
       int count = 0; 
       while (count < 1000) 
       { 
        count++; 
        Thread.Sleep(10); 
       } 

       return "Task done"; 
      }); 
    } 
+0

感謝您的評論。我已將此代碼複製到我的應用程序中。 當我在任務的某一行放置斷點時,例如:在「Thread.Sleep(10);」 然後我的主程序也會凍結。反過來也是如此。它是否正確? – Rein92

+0

如果調試器斷點遇到應用程序將始終凍結。檢測您的UI線程是否被阻止的簡單方法是調整窗口大小並移動它。另一種方法是有一個可以點擊的按鈕。如果UI被阻止,您將無法正確點擊它。 – Dbl

+0

Ahaa。當然。我沒有想到這一點。 – Rein92

1

因爲你想保持線程活着,據我所知,你不知道什麼時候或者你是否會達到1000分,異步可能是錯誤的選擇。如我錯了請糾正我。

對於你的情況,我會建議使用BackgroundWorker:

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    int count = 0; 
    BackgroundWorker worker = sender as BackgroundWorker; 
     while (!exit) 
     { 
      DateTime Time1 = DateTime.Now; 
      worker.ReportProgress(count); 
      count++; 
      if (count > 1000) 
      { 
       exit = true; 
      } 
      Thread.Sleep(10); 
     } 
} 

// This event handler updates the progress. 
     private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      resultLabel.Text = ("Background thread count = " + e.ProgressPercentage.ToString()); 
     } 

    private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     if (e.Cancelled == true) 
     { 
      resultLabel.Text = "Canceled!"; 
     } 
     else if (e.Error != null) 
     { 
      resultLabel.Text = "Error: " + e.Error.Message; 
     } 
     else 
     { 
      resultLabel.Text = "Done!"; 
     } 
    } 
+0

同意。 BackgroundWorker功能非常強大,但卻被低估了。在許多BackgroundWorker實例上運行的操作也同樣在單獨的線程上運行。 –

+0

感謝您的評論。在這種情況下,沒有必要創建一個線程,我理解這是正確的嗎? – Rein92

+0

BGW自己創建一個線程。這裏是一些更多的信息給你https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx –

0

我不明白你是否正在尋找服務或長期運行的任務。

因爲別人有長時間運行的任務,我做了一個服務

它採用了一些先進的concpets像SynchronizationContext,你應該在生產代碼中使用此之前,閱讀了很好的例子。谷歌異步等待和斯蒂芬Cleary。

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 

     var foo = new FooService(); 
     foo.StartService(); // UI thrad calling 
    } 
} 

public class FooService 
{ 
    private SynchronizationContext _context; 
    private CancellationTokenSource _cts; 
    private CancellationToken _token; 
    private Task _task; 

    public void StartService() 
    { 
     _context = SynchronizationContext.Current; // Depends on the UI thread being the one to start the service or this will fail 
     _cts = new CancellationTokenSource(10000); // Run for 10 seconds 
     _token = _cts.Token; 
     _task = Task.Run(() => Run(), _token); 
    } 

    public async Task Stop() 
    { 
     _cts.Cancel(); 
     await _task; // wait for task to finish 
    } 

    private void Run() 
    { 
     while (!_token.IsCancellationRequested) 
     { 
      // Do work     
      Thread.Sleep(1000); 
      // Alternative use Control.Invoke() if you have access to a UI element, to delegate to the UI thread 
      _context.Send((id) => Console.WriteLine($"Delegate from thread {id} to thread {Thread.CurrentThread.ManagedThreadId}"), Thread.CurrentThread.ManagedThreadId); 
     } 
    } 
}