背景: 很多小伙伴经常在群里问线程的问题,平时我经常转一些视频教程这些人不看,我就自己写个总结吧
不过还是要注意的是,切换本来就不能太频繁,要一口气改。
UI线程切换的核心思路是
1,这行代码会直接修改UI的,必须放在UI线程,掌握这条你可以自己把winform的线程检查关掉,将Control类的静态属性CheckForIllegalCrossThreadCalls设为false,必须心里有数才能做此操作
2,wpf也是如此,但是无法关闭线程检查,但是wpf的viewmodel就不需要UI线程,更新更方便,因为是内部通知的。
一,开启一个新的任务
var param = 123; //net4.5以后 Task.Run(() => { DoSomthing(param); }); Task.Run(async () => { await DoSomthingAsync(param); }); //net 4.0 Task.Factory.StartNew(delegate () { DoSomthing(param); }); //3.5 Thread t = new Thread((ThreadStart)delegate () { DoSomthing(param); }); t.Start(); //net 3.5 加线程池 ThreadPool.QueueUserWorkItem((WaitCallback)delegate (Object obj) { DoSomthing(param); });
后面都用Task为例
二, 回到UI线程
//winform Task.Run(() => { //类似sendMessage 等待发送结束 this.Invoke((Action)delegate () { DoSomthing(param); }); //类似postmessage 发了就跑 this.BeginInvoke((Action)delegate () { DoSomthing(param); }); }); //wpf Task.Run(async () => { this.Dispatcher.Invoke(delegate () { DoSomthing(param); }); //使用异步等待任务结束 await this.Dispatcher.BeginInvoke((Action)delegate () { DoSomthing(param); }); //使用抛弃返回值的方式,直接过 不等待 _ = this.Dispatcher.BeginInvoke((Action)delegate () { DoSomthing(param); }); });
await Task.Run(async () => { DoSomething(); } //回到调用线程
三, 高级方法
1 使用 SynchronizationContext
//在UI线程时记录下上下文 var syncContext = SynchronizationContext.Current; Task.Run(() => { //使用UI线程 syncContext.Post(o => { DoSomthing(param); }, null); });
2 await一个SynchronizationContext
//在UI线程时记录下上下文 var syncContext = SynchronizationContext.Current; Task.Run(() => { //回到UI线程 await syncContext; DoSomthing(param);//在UI线程中 });
那么如何await一个SynchronizationContext呢?
//写个Awaiter类 public sealed class SynchronizationContextAwaiter : INotifyCompletion { private readonly SynchronizationContext _context; public SynchronizationContextAwaiter(SynchronizationContext context) => _context = context ?? throw new ArgumentNullException("context"); public bool IsCompleted => SynchronizationContext.Current == _context; public void OnCompleted(Action action) => _context.Post(x => action(), null); public void GetResult() { } } //接下去使用awaiter类,写个扩展方法 public static SynchronizationContextAwaiter GetAwaiter(this SynchronizationContext context) { return new SynchronizationContextAwaiter(context); }
四,将三的内容整合到一个类中
需要在UI线程初始化:
UIThreadContext.Init();
using Ruifei.Common.UIThread; using System; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; public static class UIThreadContext { public static SynchronizationContext Context; public static Thread UIThread => uiThread; static Thread uiThread = null; public static void SendUIThread(Action action) { if (Context != null) Context.Send(x => action(), null); else action(); } public static void SendUIThreadIfRequired(Action action) { Thread crt = Thread.CurrentThread; if (uiThread != crt) SendUIThread(action); else action(); } public static void PostUIThread(Action action) { if (Context != null) Context.Post(x => action(), null); else action(); } public static void PostUIThread(Func<Task> action) { if (Context != null) Context.Post(x => action(), null); else action(); } public static void PostUIThreadIfRequired(Action action) { if (IsInUIThread()) action(); else PostUIThread(action); } public static void PostUIThreadIfRequired(Func<Task> action) { if (IsInUIThread()) action(); else PostUIThread(action); } public static bool IsInUIThread() { return uiThread == Thread.CurrentThread; } static Dispatcher dispatcher = (Application.Current != null && Application.Current.Dispatcher != null) ? Application.Current.Dispatcher : Dispatcher.CurrentDispatcher; public async static void InvokeOnUIThread(Func<Task> action) { if (dispatcher == null || dispatcher.CheckAccess()) await action(); else await dispatcher.BeginInvoke(action); } public static void InvokeOnUIThread(Action action) { if (dispatcher == null || dispatcher.CheckAccess()) action(); else dispatcher.BeginInvoke(action); } public static void Init() { Context = SynchronizationContext.Current; uiThread = Thread.CurrentThread; } public static SynchronizationContextAwaiter GetAwaiter(this SynchronizationContext context) { return new SynchronizationContextAwaiter(context); } /// <summary> /// 切换到UI线程 /// </summary> /// <returns></returns> public static SynchronizationContext SwitchToUIThread() { return UIThreadContext.Context; } /// <summary> /// WithoutContext /// </summary> /// <returns></returns> public static WaitForSwitchToNewTask SwitchToNewTask() { return new WaitForSwitchToNewTask(false); } } namespace Ruifei.Common.UIThread { public class WaitForSwitchToNewTask { bool withContext = false; public WaitForSwitchToNewTask(bool _with) { withContext = _with; } public ConfiguredTaskAwaitable.ConfiguredTaskAwaiter GetAwaiter() { return Task.Run(() => { }).ConfigureAwait(withContext).GetAwaiter(); } } public sealed class SynchronizationContextAwaiter : INotifyCompletion { private readonly SynchronizationContext _context; public SynchronizationContextAwaiter(SynchronizationContext context) => _context = context ?? throw new ArgumentNullException("context"); public bool IsCompleted => SynchronizationContext.Current == _context; public void OnCompleted(Action action) { if (_context == null) { action(); } else { _context.Send(x => action(), null); } } public void GetResult() { } } }