C# Task 与 SynchronizationContext

示例代码

复制代码
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace Test
{
    internal class Program
    {
        static void Main(string[] args)
        {
            _mainThreadSynchronizationContext = new ThreadSynchronizationContext("main");
            _threadSynchronizationContext = new ThreadSynchronizationContext("thread");

            if (SynchronizationContext.Current == null)
            {
                Console.WriteLine("Main SynchronizationContext.Current is null");
            }
            SynchronizationContext.SetSynchronizationContext(_mainThreadSynchronizationContext);
            if (SynchronizationContext.Current != null)
            {
                Console.WriteLine("Main SynchronizationContext.Current is not null!");
            }
            
            Thread thread = new Thread(ThreadLoop);
            thread.Start();
            MainLoop();
        }

        private static ThreadSynchronizationContext _mainThreadSynchronizationContext;

        private static ThreadSynchronizationContext _threadSynchronizationContext;
        
        private static void MainLoop()
        {
            MainTest();
            while (true)
            {
                Thread.Sleep(1);
                _mainThreadSynchronizationContext.Update();
            }
        }

        private static void ThreadLoop()
        {
            if (SynchronizationContext.Current == null)
            {
                Console.WriteLine("Thread SynchronizationContext.Current is null");
            }
            SynchronizationContext.SetSynchronizationContext(_threadSynchronizationContext);
            if (SynchronizationContext.Current != null)
            {
                Console.WriteLine("Thread SynchronizationContext.Current is not null");
            }
            Thread.Sleep(10000);
            ThreadTest();
            while (true)
            {
                Thread.Sleep(1);
                _threadSynchronizationContext.Update();
            }
        }

        private static async Task MainTest()
        {
            Console.WriteLine($"MainTest {Thread.CurrentThread.ManagedThreadId}");
            await TaskNew();
            Console.WriteLine($"MainTest End {Thread.CurrentThread.ManagedThreadId}");
        }
        
        private static async Task ThreadTest()
        {
            Console.WriteLine($"ThreadTest {Thread.CurrentThread.ManagedThreadId}");
            await TaskNew();
            Console.WriteLine($"ThreadTest End {Thread.CurrentThread.ManagedThreadId}");
        }
        
        private static Task TaskNew()
        {
            return Task.Factory.StartNew(() =>
            {
                Console.WriteLine($"TaskNew Begin {Thread.CurrentThread.ManagedThreadId}");
                Thread.Sleep(5000);
                Console.WriteLine($"TaskNew End {Thread.CurrentThread.ManagedThreadId}");
            });
        }

        public class ThreadSynchronizationContext : SynchronizationContext
        {
            // 线程同步队列,发送接收socket回调都放到该队列,由poll线程统一执行
            private readonly ConcurrentQueue<Action> queue = new();

            private Action a;

            private string name;

            public ThreadSynchronizationContext(string name)
            {
                this.name = name;
            }

            public void Update()
            {
                while (true)
                {
                    if (!this.queue.TryDequeue(out a))
                    {
                        return;
                    }

                    try
                    {
                        a();
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e);
                    }
                }
            }

            public override void Post(SendOrPostCallback callback, object state)
            {
                this.Post(() => callback(state));
            }
		
            public void Post(Action action)
            {
                Console.WriteLine($"Post: {name}");
                this.queue.Enqueue(action);
            }
        }
        
        
    }
}

输出结果

复制代码
Thread SynchronizationContext.Current is null
Thread SynchronizationContext.Current is not null
MainTest 1
TaskNew Begin 4
TaskNew End 4
Post: main
MainTest End 1
ThreadTest 3
TaskNew Begin 5
TaskNew End 5
Post: thread
ThreadTest End 3

SynchronizationContext

这个类的作用是将代码从一个线程转移到另外一个线程执行。

Send 表示同步执行代码快,会阻塞当前线程,直到Send执行完成。

Post 表示异步执行代码快,不阻塞当前线程。

SynchronizationContext.Current 与 SynchronizationContext.SetSynchronizationContext

这个静态属性与方法,是线程隔离的,也就是线程1 调用SynchronizationContext.SetSynchronizationContext。线程2 通过 SynchronizationContext.Current 是获取不到值的,从上面例子中也可以看出。

可以看到 SynchronizationContext 的源码,Send 方法是当前线程同步调用。Post 是从线程池中找一个线程进行调用。

SynchronizationContext.Current 这个值在不同环境中的主线程结果是不一样的。在控制台情况下默认是NULL,在WindowForm的情况下是 WindowsFormsSynchronizationContext。在Unity中是 UnitySynchronizationContext。

当通过await Task开启一个异步任务,当任务完成后会调用SynchronizationContext.Current.Post 方法。在例子中可以看出。如果SynchronizationContext.Current 是空,则会使用默认值SynchronizationContext对象。通过这个特性,重写SynchronizationContext 就可以让线程执行完成Task后,回调到原来线程进行执行,这个经常需要使用到。

相关推荐
小杨不想秃头11 小时前
信息安全工程师教程第二章密码学
运维·服务器·密码学
I'mChloe11 小时前
WGCLOUD怎么监控多台服务器?从Server、Agent 部署到监控面板实战
运维·服务器
Android系统攻城狮12 小时前
Linux PipeWire深度解析之pw_init调用流程与实战(八十三)
linux·运维·服务器·音频进阶·pipewire音频进阶
惊鸿醉12 小时前
Unity 实战:用讯飞 WebAPI 做一个“说普通话、播方言“的语音程序
unity·c#·游戏引擎·语音识别
略略略咯咯13 小时前
C#Unity
开发语言·unity·c#
zbyyd13 小时前
Linux 进程管理详解:从概念到实战
linux·运维·服务器
Behavior14 小时前
Unity文字显示带竖线问题
c#·unity3d
云飞云共享云桌面15 小时前
智能装备工厂研发团队如何利用单台服务器承载 10 路 SolidWorks 并发?
运维·服务器·3d·自动化·制造
辻弋20115 小时前
手动关服务会自动重启、组策略家庭版用不了——Windows Update Blocker用“禁用+锁定”双保险,让Win10/11自动更新彻底闭嘴
服务器·人工智能·windows·电脑·开源软件
躺不平的理查德16 小时前
OpenSSL 的异步 TLS 1.3 加密
linux·运维·服务器