2017-10-21 277 views
4

我有一個C#控制檯應用程序,可以通過TCP套接字連接輸入其中的內容。當通過套接字接收函數接收輸入時,如何切換到主線程?在C#控制檯應用程序中更改線程上下文

所以類似這樣的事情在WPF:

public void TaskDispatcher() 
{ 
    if (DispatcherObjectForTaskDispatcher.Thread != System.Threading.Thread.CurrentThread) 
     DispatcherObjectForTaskDispatcher.Invoke(new TaskDispatcherDelegate(TaskDispatcher)); 
    else 
    { 
     // Do some thing in the UI thread 
    } 
} 
+2

不是很清楚。控制檯應用程序沒有UI,因此沒有「UI線程」。它也沒有SyncContext。你可能只需要一個生產者/消費者設置或東西。 –

+0

當然不是,而是一個主線程,其中靜態void Main(string [] args)函數運行的線程和receive函數具有不同的線程。帶UI線程的只是一個例子。 – uhwgmxorg

+0

但是,生產者 - 消費者數據流模式似乎是正確的提示。 – uhwgmxorg

回答

1

只需使用一個Producer-Consumer模式如下面的工作示例。從其他線程排隊作業,並讓主線程從作業隊列中處理排隊的作業。

我使用的計時器螺紋和用戶輸入線程來模擬2個線程生產作業。您可以實現您的TCP事件,以將作業排入作業隊列。您應該將任何相關對象作爲參數存儲在作業中,以供日後處理。您還必須定義一個由作業調用的函數,該函數將在主線程中運行。

這裏使用的主線程僅僅是爲了離職和處理它們,但如果你稍微改進一下這些代碼,你可以使用任何其他線程來達到這個目的。

你甚至可以實現多線程處理,其中更多的處理線程從相同的作業隊列中出隊。請注意,這會帶來新的併發問題,您可能需要處理。這是在應用程序中獲得更多處理能力的缺點。一些場景適用於多線程處理(例如視頻/圖像處理),而其他場景則不適用。

下面的代碼是寫在一個的Visual Studio 2017年DOTNET 4.6.1控制檯應用程序項目一個完整的工作示例。只需複製,粘貼並按F5即可。

using System; 
using System.Collections.Concurrent; 
using System.Diagnostics; 
using System.Threading; 

// Compiled and tested in: Visual Studio 2017, DotNET 4.6.1 

namespace MyNamespace 
{ 
    public class Program 
    { 
     public static void Main(string[] args) 
     { 
      MyApplication app = new MyApplication(); 
      app.Run(); 
     } 
    } 

    public class MyApplication 
    { 
     private BlockingCollection<Job> JobQueue = new BlockingCollection<Job>(); 
     private CancellationTokenSource JobCancellationTokenSource = new CancellationTokenSource(); 
     private CancellationToken JobCancellationToken; 
     private Timer Timer; 
     private Thread UserInputThread; 



     public void Run() 
     { 
      // Give a name to the main thread: 
      Thread.CurrentThread.Name = "Main"; 

      // Fires a Timer thread: 
      Timer = new Timer(new TimerCallback(TimerCallback), null, 1000, 2000); 

      // Fires a thread to read user inputs: 
      UserInputThread = new Thread(new ThreadStart(ReadUserInputs)) 
      { 
       Name = "UserInputs", 
       IsBackground = true 
      }; 
      UserInputThread.Start(); 

      // Prepares a token to cancel the job queue: 
      JobCancellationToken = JobCancellationTokenSource.Token; 

      // Start processing jobs: 
      ProcessJobs(); 

      // Clean up: 
      JobQueue.Dispose(); 
      Timer.Dispose(); 
      UserInputThread.Abort(); 

      Console.WriteLine("Done."); 
     } 



     private void ProcessJobs() 
     { 
      try 
      { 
       // Checks if the blocking collection is still up for dequeueing: 
       while (!JobQueue.IsCompleted) 
       { 
        // The following line blocks the thread until a job is available or throws an exception in case the token is cancelled: 
        JobQueue.Take(JobCancellationToken).Run(); 
       } 
      } 
      catch { } 
     } 



     private void ReadUserInputs() 
     { 
      // User input thread is running here. 
      ConsoleKey key = ConsoleKey.Enter; 

      // Reads user inputs and queue them for processing until the escape key is pressed: 
      while ((key = Console.ReadKey(true).Key) != ConsoleKey.Escape) 
      { 
       Job userInputJob = new Job("UserInput", this, new Action<ConsoleKey>(ProcessUserInputs), key); 
       JobQueue.Add(userInputJob); 
      } 
      // Stops processing the JobQueue: 
      JobCancellationTokenSource.Cancel(); 
     } 

     private void ProcessUserInputs(ConsoleKey key) 
     { 
      // Main thread is running here. 
      Console.WriteLine($"You just typed '{key}'. (Thread: {Thread.CurrentThread.Name})"); 
     } 



     private void TimerCallback(object param) 
     { 
      // Timer thread is running here. 
      Job job = new Job("TimerJob", this, new Action<string>(ProcessTimer), "A job from timer callback was processed."); 
      JobQueue.TryAdd(job); // Just enqueues the job for later processing 
     } 

     private void ProcessTimer(string message) 
     { 
      // Main thread is running here. 
      Console.WriteLine($"{message} (Thread: {Thread.CurrentThread.Name})"); 
     } 
    } 



    /// <summary> 
    /// The Job class wraps an object's method call, with or without arguments. This method is called later, during the Job execution. 
    /// </summary> 
    public class Job 
    { 
     public string Name { get; } 
     private object TargetObject; 
     private Delegate TargetMethod; 
     private object[] Arguments; 

     public Job(string name, object obj, Delegate method, params object[] args) 
     { 
      Name = name; 
      TargetObject = obj; 
      TargetMethod = method; 
      Arguments = args; 
     } 

     public void Run() 
     { 
      try 
      { 
       TargetMethod.Method.Invoke(TargetObject, Arguments); 
      } 
      catch(Exception ex) 
      { 
       Debug.WriteLine($"Unexpected error running job '{Name}': {ex}"); 
      } 
     } 

    } 
} 
+0

嗨Daniel非常好的模板,謝謝! – uhwgmxorg