c# 关于同步线程上下文
前言什么是同步线程上下文呢?
这里面有一些需求,就是有些代码需要在同一个线程运行。
避免一些并发问题啥的。
正文
例子:
自定义上下文:
public class SingleThreadSynchronizationContext : SynchronizationContext, IDisposable
{
private readonly BlockingCollection<(SendOrPostCallback Callback, object State)> _queue = new();
private readonly Thread _processingThread;
public SingleThreadSynchronizationContext()
{
_processingThread = new Thread(ProcessQueue)
{
IsBackground = true,
Name = "SingleThreadSynchronizationContext Thread"
};
_processingThread.Start();
}
// 异步方式派发工作
public override void Post(SendOrPostCallback d, object state)
{
_queue.Add((d, state));
}
// 同步方式派发工作(可能阻塞)
public override void Send(SendOrPostCallback d, object state)
{
if (Thread.CurrentThread == _processingThread)
{
d(state); // 如果已经在目标线程,直接执行
}
else
{
using var signal = new ManualResetEventSlim();
Post(_ =>
{
d(_);
signal.Set();
}, state);
signal.Wait();
}
}
private void ProcessQueue()
{
// 设置当前线程的上下文
SetSynchronizationContext(this);
Console.WriteLine(SynchronizationContext.Current == null);
Console.WriteLine($"ProcessQueue: {Thread.CurrentThread.ManagedThreadId}");
foreach (var work in _queue.GetConsumingEnumerable())
{
work.Callback(work.State);
}
}
public void Dispose()
{
_queue.CompleteAdding();
_processingThread.Join();
_queue.Dispose();
}
}然后去取:
static async Task Main()
{
Console.WriteLine($"Main Thread: {Thread.CurrentThread.ManagedThreadId}");
// 创建并设置自定义上下文
using var context = new SingleThreadSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(context);
// 演示异步操作如何回到特定线程
await Task.Run(() =>
{
Console.WriteLine(SynchronizationContext.Current == null);
Console.WriteLine($"Task.Run Thread: {Thread.CurrentThread.ManagedThreadId}");
}).ConfigureAwait(true);
Console.WriteLine($"After await Thread: {Thread.CurrentThread.ManagedThreadId}");
// 演示Send/Post区别
context.Post(_ =>
{
Console.WriteLine($"Post Thread: {Thread.CurrentThread.ManagedThreadId}");
}, null);
context.Send(_ =>
{
Console.WriteLine($"Send Thread: {Thread.CurrentThread.ManagedThreadId}");
}, null);
Console.WriteLine("All operations completed");
Console.ReadKey();
}这样就可以同步上下文了,也就是在指定的上下文中执行了。
taskschedule 方式:
// 自定义单线程任务调度器
public class SingleThreadTaskScheduler : TaskScheduler, IDisposable
{
private readonly BlockingCollection<Task> _tasks = new();
private readonly Thread _thread;
public SingleThreadTaskScheduler(string threadName = null)
{
_thread = new Thread(() =>
{
foreach (var task in _tasks.GetConsumingEnumerable())
{
TryExecuteTask(task);
}
})
{
IsBackground = true,
Name = threadName ?? "SingleThreadTaskScheduler"
};
_thread.Start();
}
protected override IEnumerable<Task> GetScheduledTasks() => _tasks.ToArray();
protected override void QueueTask(Task task) => _tasks.Add(task);
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) =>
Thread.CurrentThread == _thread && TryExecuteTask(task);
public void Dispose()
{
_tasks.CompleteAdding();
_thread.Join();
}
}
// 使用示例
async Task RunWithTaskScheduler()
{
using var scheduler = new SingleThreadTaskScheduler("CustomTaskSchedulerThread");
await Task.Factory.StartNew(async () =>
{
Console.WriteLine($"Current Thread: {Thread.CurrentThread.Name}");
await Task.Delay(100);
Console.WriteLine($"Still on thread: {Thread.CurrentThread.Name}");
}, CancellationToken.None, TaskCreationOptions.None, scheduler).Unwrap();
}这里面是原理,然后呢?实际上,有一个东西是taskschedule 默认给我们提供了一个同步的上下文的调度器。
internal sealed class MyForm : Form {
private readonly TaskScheduler m_syncContextTaskScheduler;
public MyForm() {
// 获得对一个同步上下文任务调度器的引用
m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Text = "Synchronization Context Task Scheduler Demo";
Visible = true; Width = 400; Height = 100;
}
private CancellationTokenSource m_cts;
protected override void OnMouseClick(MouseEventArgs e) {
if (m_cts != null) { // 一个操作正在进行,取消它
m_cts.Cancel();
m_cts = null;
} else { // 操作没有开始,启动它
// 操作没有开始,启动它
Text = "Operation running";
m_cts = new CancellationTokenSource();
// 这个任务使用默认任务调度器,在一个线程池线程上执行
Task<Int32> t = Task.Run(() => Sum(m_cts.Token, 20000), m_cts.Token);
// 这些任务使用同步上下文任务调度器,在 GUI 线程上执行
t.ContinueWith(task => Text = "Result: " + task.Result,
CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, m_syncContextTaskScheduler);
t.ContinueWith(task => Text = "Operation canceled",
CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled, m_syncContextTaskScheduler);
t.ContinueWith(task => Text = "Operation faulted",
CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, m_syncContextTaskScheduler);
}
base.OnMouseClick(e);
}
}是的就是这个:TaskScheduler.FromCurrentSynchronizationContext(), 差不多和上面写的一样,可以自行去阅读源码
结
同步上下文大概就是这么回事,不是去线程池同步上下文,线程池最好是没有上下文的,这样才符合上下文的特征。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]