背景: 很多小伙伴經常在群里問線程的問題,平時我經常轉一些視頻教程這些人不看,我就自己寫個總結吧
不過還是要注意的是,切換本來就不能太頻繁,要一口氣改。
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() { } } }