在C#控制台应用程序中更改线程上下文
我有一个C#控制台应用程序,在其中我可以通过TCP套接字连接获取输入。当我通过套接字上的接收函数接收输入时,如何切换到主线程 类似于WPF中的类似内容:在C#控制台应用程序中更改线程上下文,c#,multithreading,console,console-application,C#,Multithreading,Console,Console Application,我有一个C#控制台应用程序,在其中我可以通过TCP套接字连接获取输入。当我通过套接字上的接收函数接收输入时,如何切换到主线程 类似于WPF中的类似内容: public void TaskDispatcher() { if (DispatcherObjectForTaskDispatcher.Thread != System.Threading.Thread.CurrentThread) DispatcherObjectForTaskDispatcher.Invoke(ne
public void TaskDispatcher()
{
if (DispatcherObjectForTaskDispatcher.Thread != System.Threading.Thread.CurrentThread)
DispatcherObjectForTaskDispatcher.Invoke(new TaskDispatcherDelegate(TaskDispatcher));
else
{
// Do some thing in the UI thread
}
}
只需使用下面的工作示例中的模式。将作业从其他线程排队,并让主线程处理作业队列中排队的作业
我使用了一个计时器线程和一个用户输入线程来模拟两个产生作业的线程。您可以实现您的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}");
}
}
}
}
使用系统;
使用System.Collections.Concurrent;
使用系统诊断;
使用系统线程;
//编译和测试于:Visual Studio 2017,DotNET 4.6.1
名称空间MyNamespace
{
公共课程
{
公共静态void Main(字符串[]args)
{
MyApplication app=新的MyApplication();
app.Run();
}
}
公共类MyApplication
{
private BlockingCollection JobQueue=new BlockingCollection();
私有CancellationTokenSource作业CancellationTokenSource=新的CancellationTokenSource();
私有取消令牌JobCancellationToken;
私人定时器;
私有线程UserInputThread;
公开募捐
{
//为主线程指定一个名称:
Thread.CurrentThread.Name=“Main”;
//触发计时器线程:
计时器=新计时器(新TimerCallback(TimerCallback),null,1000,2000);
//激发线程以读取用户输入:
UserInputThread=新线程(新线程开始(ReadUserInputs))
{
Name=“UserInputs”,
IsBackground=true
};
UserInputThread.Start();
//准备令牌以取消作业队列:
JobCancellationToken=JobCancellationTokenSource.Token;
//开始处理作业:
ProcessJobs();
//清理:
JobQueue.Dispose();
Timer.Dispose();
UserInputThread.Abort();
控制台。WriteLine(“完成”);
}
私有void ProcessJobs()
{
尝试
{
//检查阻止集合是否仍在排队:
而(!JobQueue.IsCompleted)
{
//以下行阻塞线程,直到作业可用,或在令牌取消时引发异常:
JobQueue.Take(JobCancellationToken.Run();
}
}
捕获{}
}
私有void ReadUserInputs()
{
//用户输入线程正在这里运行。
ConsoleKey key=ConsoleKey.Enter;
//读取用户输入并将其排队等待处理,直到按下escape键:
while((key=Console.ReadKey(true.key)!=ConsoleKey.Escape)
{
Job userInputJob=新作业(“UserInput”,此,新操作(ProcessUserInputs),键);
添加(userInputJob);
}
//停止处理作业队列:
JobCancellationTokenSource.Cancel();
}
私有无效ProcessUserInputs(控制台密钥)
{
//主线程正在这里运行。
WriteLine($“您刚刚键入了“{key}”。(线程:{Thread.CurrentThread.Name})”;
}
私有void TimerCallback(对象参数)
{
//计时器线程正在这里运行。
Job Job=新作业(“TimerJob”,这是新操作(ProcessTimer),“已处理来自计时器回调的作业”);
JobQueue.TryAdd(job);//只需将作业排入队列以便稍后处理
}
私有void ProcessTimer(字符串消息)
{
//主线程正在这里运行。
WriteLine($“{message}(线程:{Thread.CurrentThread.Name})”;
}
}
///
///Job类包装对象的方法调用(带参数或不带参数)。此方法稍后在作业执行期间调用。
///
公开课工作
{
公共字符串名称{get;}
私有对象目标对象;
私有委托目标方法;
私有对象[]参数;
公共作业(字符串名称、对象对象、委托方法、参数对象[]args)
{
名称=名称;
TargetObject=obj;
TargetMethod=方法;
参数=args;
}
公开募捐
{
尝试
{
TargetMethod.Method.Invoke(TargetObject,参数);
}
捕获(例外情况除外)
{
Debug.WriteLine($“运行作业{Name}时发生意外错误:{ex}”);
}
}
}
}
不太清楚。控制台应用程序没有UI,因此没有“UI线程”。它也没有同步上下文。你可能