只需使用一個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}");
}
}
}
}
不是很清楚。控制檯應用程序沒有UI,因此沒有「UI線程」。它也沒有SyncContext。你可能只需要一個生產者/消費者設置或東西。 –
當然不是,而是一個主線程,其中靜態void Main(string [] args)函數運行的線程和receive函數具有不同的線程。帶UI線程的只是一個例子。 – uhwgmxorg
但是,生產者 - 消費者數據流模式似乎是正確的提示。 – uhwgmxorg