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

 

posted @ 2020-02-17 14:52  咖喱gg  阅读(1021)  评论(0编辑  收藏  举报