winform和wpf里必知的多線程知識


背景: 很多小伙伴經常在群里問線程的問題,平時我經常轉一些視頻教程這些人不看,我就自己寫個總結吧

不過還是要注意的是,切換本來就不能太頻繁,要一口氣改。

 

 

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() { }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM