找回密码
 立即注册
首页 业界区 安全 c# 关于同步线程上下文

c# 关于同步线程上下文

米嘉怡 2025-6-1 18:12:29
前言

什么是同步线程上下文呢?
这里面有一些需求,就是有些代码需要在同一个线程运行。
避免一些并发问题啥的。
正文

例子:
自定义上下文:
  1. public class SingleThreadSynchronizationContext : SynchronizationContext, IDisposable
  2. {
  3.     private readonly BlockingCollection<(SendOrPostCallback Callback, object State)> _queue = new();
  4.     private readonly Thread _processingThread;
  5.     public SingleThreadSynchronizationContext()
  6.     {
  7.         _processingThread = new Thread(ProcessQueue)
  8.         {
  9.             IsBackground = true,
  10.             Name = "SingleThreadSynchronizationContext Thread"
  11.         };
  12.         _processingThread.Start();
  13.     }
  14.     // 异步方式派发工作
  15.     public override void Post(SendOrPostCallback d, object state)
  16.     {
  17.         _queue.Add((d, state));
  18.     }
  19.     // 同步方式派发工作(可能阻塞)
  20.     public override void Send(SendOrPostCallback d, object state)
  21.     {
  22.         if (Thread.CurrentThread == _processingThread)
  23.         {
  24.             d(state); // 如果已经在目标线程,直接执行
  25.         }
  26.         else
  27.         {
  28.             using var signal = new ManualResetEventSlim();
  29.             Post(_ =>
  30.             {
  31.                 d(_);
  32.                 signal.Set();
  33.             }, state);
  34.             signal.Wait();
  35.         }
  36.     }
  37.     private void ProcessQueue()
  38.     {
  39.         // 设置当前线程的上下文
  40.         SetSynchronizationContext(this);
  41.         Console.WriteLine(SynchronizationContext.Current == null);
  42.         Console.WriteLine($"ProcessQueue: {Thread.CurrentThread.ManagedThreadId}");
  43.         foreach (var work in _queue.GetConsumingEnumerable())
  44.         {
  45.             work.Callback(work.State);
  46.         }
  47.     }
  48.     public void Dispose()
  49.     {
  50.         _queue.CompleteAdding();
  51.         _processingThread.Join();
  52.         _queue.Dispose();
  53.     }
  54. }
复制代码
然后去取:
  1. static async Task Main()
  2. {
  3.         Console.WriteLine($"Main Thread: {Thread.CurrentThread.ManagedThreadId}");
  4.         // 创建并设置自定义上下文
  5.         using var context = new SingleThreadSynchronizationContext();
  6.         SynchronizationContext.SetSynchronizationContext(context);
  7.         // 演示异步操作如何回到特定线程
  8.         await Task.Run(() =>
  9.         {
  10.                 Console.WriteLine(SynchronizationContext.Current == null);
  11.                 Console.WriteLine($"Task.Run Thread: {Thread.CurrentThread.ManagedThreadId}");
  12.         }).ConfigureAwait(true);
  13.         Console.WriteLine($"After await Thread: {Thread.CurrentThread.ManagedThreadId}");
  14.         // 演示Send/Post区别
  15.         context.Post(_ =>
  16.         {
  17.                 Console.WriteLine($"Post Thread: {Thread.CurrentThread.ManagedThreadId}");
  18.         }, null);
  19.         context.Send(_ =>
  20.         {
  21.                 Console.WriteLine($"Send Thread: {Thread.CurrentThread.ManagedThreadId}");
  22.         }, null);
  23.         Console.WriteLine("All operations completed");
  24.        
  25.         Console.ReadKey();
  26. }
复制代码
这样就可以同步上下文了,也就是在指定的上下文中执行了。
taskschedule 方式:
  1. // 自定义单线程任务调度器
  2. public class SingleThreadTaskScheduler : TaskScheduler, IDisposable
  3. {
  4.     private readonly BlockingCollection<Task> _tasks = new();
  5.     private readonly Thread _thread;
  6.     public SingleThreadTaskScheduler(string threadName = null)
  7.     {
  8.         _thread = new Thread(() =>
  9.         {
  10.             foreach (var task in _tasks.GetConsumingEnumerable())
  11.             {
  12.                 TryExecuteTask(task);
  13.             }
  14.         })
  15.         {
  16.             IsBackground = true,
  17.             Name = threadName ?? "SingleThreadTaskScheduler"
  18.         };
  19.         _thread.Start();
  20.     }
  21.     protected override IEnumerable<Task> GetScheduledTasks() => _tasks.ToArray();
  22.     protected override void QueueTask(Task task) => _tasks.Add(task);
  23.     protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) =>
  24.         Thread.CurrentThread == _thread && TryExecuteTask(task);
  25.     public void Dispose()
  26.     {
  27.         _tasks.CompleteAdding();
  28.         _thread.Join();
  29.     }
  30. }
  31. // 使用示例
  32. async Task RunWithTaskScheduler()
  33. {
  34.     using var scheduler = new SingleThreadTaskScheduler("CustomTaskSchedulerThread");
  35.    
  36.     await Task.Factory.StartNew(async () =>
  37.     {
  38.         Console.WriteLine($"Current Thread: {Thread.CurrentThread.Name}");
  39.         await Task.Delay(100);
  40.         Console.WriteLine($"Still on thread: {Thread.CurrentThread.Name}");
  41.     }, CancellationToken.None, TaskCreationOptions.None, scheduler).Unwrap();
  42. }
复制代码
这里面是原理,然后呢?实际上,有一个东西是taskschedule 默认给我们提供了一个同步的上下文的调度器。
  1. internal sealed class MyForm : Form {
  2.     private readonly TaskScheduler m_syncContextTaskScheduler;
  3.     public MyForm() {
  4.         // 获得对一个同步上下文任务调度器的引用
  5.         m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
  6.         Text = "Synchronization Context Task Scheduler Demo";
  7.         Visible = true; Width = 400; Height = 100;
  8.     }
  9.     private CancellationTokenSource m_cts;
  10.     protected override void OnMouseClick(MouseEventArgs e) {
  11.         if (m_cts != null) {    // 一个操作正在进行,取消它
  12.             m_cts.Cancel();
  13.             m_cts = null;
  14.         } else {    // 操作没有开始,启动它
  15.             // 操作没有开始,启动它
  16.             Text = "Operation running";
  17.             m_cts = new CancellationTokenSource();
  18.             // 这个任务使用默认任务调度器,在一个线程池线程上执行
  19.             Task<Int32> t = Task.Run(() => Sum(m_cts.Token, 20000), m_cts.Token);
  20.             // 这些任务使用同步上下文任务调度器,在 GUI 线程上执行
  21.             t.ContinueWith(task => Text = "Result: " + task.Result,
  22.                 CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, m_syncContextTaskScheduler);
  23.             t.ContinueWith(task => Text = "Operation canceled",
  24.                 CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled, m_syncContextTaskScheduler);
  25.             t.ContinueWith(task => Text = "Operation faulted",
  26.                 CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, m_syncContextTaskScheduler);
  27.         }
  28.         base.OnMouseClick(e);
  29.     }
  30. }
复制代码
是的就是这个:TaskScheduler.FromCurrentSynchronizationContext(), 差不多和上面写的一样,可以自行去阅读源码


同步上下文大概就是这么回事,不是去线程池同步上下文,线程池最好是没有上下文的,这样才符合上下文的特征。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册