2017-10-21 278 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