米嘉怡 发表于 2025-6-1 18:12:29

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]
查看完整版本: c# 关于同步线程上下文