只需像下面的工作示例中使用
生产者-消费者模式。从其他线程排队作业,让主线程从作业队列处理排队作业。
我使用了一个
定时器线程和一个
用户输入线程来模拟2个线程生成作业。您可以实现TCP事件,只需在作业队列中排队作业即可。您应该将任何相关对象作为参数存储在作业中,以供稍后处理。您还必须定义一个由作业调用的函数,在主线程中运行。
这里的主线程仅用于出队作业并处理它们,但是如果您稍微改进此代码,可以使用任何其他线程来完成此任务。
您甚至可以实现多线程处理,其中更多的处理线程从同一作业队列出队。请注意,这会带来新的并发问题,您可能需要处理这些问题。这是在应用程序中获得更多处理能力的缺点。某些情况适合多线程处理(例如视频/图像处理),而有些情况则不适合。
以下代码是一个完整的工作示例,使用Visual Studio 2017、DotNET 4.6.1和控制台应用程序项目编写。只需复制、粘贴并按F5即可。
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading;
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()
{
Thread.CurrentThread.Name = "Main";
Timer = new Timer(new TimerCallback(TimerCallback), null, 1000, 2000);
UserInputThread = new Thread(new ThreadStart(ReadUserInputs))
{
Name = "UserInputs",
IsBackground = true
};
UserInputThread.Start();
JobCancellationToken = JobCancellationTokenSource.Token;
ProcessJobs();
JobQueue.Dispose();
Timer.Dispose();
UserInputThread.Abort();
Console.WriteLine("Done.");
}
private void ProcessJobs()
{
try
{
while (!JobQueue.IsCompleted)
{
JobQueue.Take(JobCancellationToken).Run();
}
}
catch { }
}
private void ReadUserInputs()
{
ConsoleKey key = ConsoleKey.Enter;
while ((key = Console.ReadKey(true).Key) != ConsoleKey.Escape)
{
Job userInputJob = new Job("UserInput", this, new Action<ConsoleKey>(ProcessUserInputs), key);
JobQueue.Add(userInputJob);
}
JobCancellationTokenSource.Cancel();
}
private void ProcessUserInputs(ConsoleKey key)
{
Console.WriteLine($"You just typed '{key}'. (Thread: {Thread.CurrentThread.Name})");
}
private void TimerCallback(object param)
{
Job job = new Job("TimerJob", this, new Action<string>(ProcessTimer), "A job from timer callback was processed.");
JobQueue.TryAdd(job);
}
private void ProcessTimer(string message)
{
Console.WriteLine($"{message} (Thread: {Thread.CurrentThread.Name})");
}
}
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}");
}
}
}
}