• C#多线程,基础知识很重要


    本文通过介绍C#多线程的用法(基础玩法),附加介绍一下WinForm里边跨线程访问UI的方法

    如图,就是这么一个简单的界面,每个按钮下面一个方法,分别设置文本框里边的内容,那么,开始吧!

    先介绍一下WinForm的线程模型:WinForm 是通过调用Windows API 的GetMessage Or PeekMeeage来处理其他线程发送过来的消息,这些消息存储在系统的一个消息队列中,创建主界面的线程就是主线程(UI线程),UI线程负责消费该消息队列中的消息。

    WinForm框架中有一个ISynchronizeInvoke接口,所有的UI元素都继承自该接口,接口中的InvokeRequired属性表示了当前线程是否是创建它的线程,接口中的BeginInvoke or Invoke 负责将消息发送到消息队列,这样UI线程就能够正确的访问它了。

    那么,首先看代码片段一:里边就实现了将设置文本框内容的消息发送到了消息队列

    private void SetMessage(string message)
            {
                if (this.txtMsg.InvokeRequired)
                {
                    //BeginInvoke or Invoke 负责将消息发送到消息队列
                    this.txtMsg.BeginInvoke(new Action<string>((msg) =>
                    {
                        this.txtMsg.Text = msg;
                    }), message);
                }
                else
                {
                    this.txtMsg.Text = message;
                }
    
            }

    代码片段二Thread:Thread可能是用的最多的了,也是最早的框架里边就有的。这种写法很简单,也很方便,需要提一下的就是IsBackground属性,IsBackground=true表示为后台线程,应用程序退出,哪怕任务没有执行完,也会退出;IsBackground=false表示为前台线程,默认为false,应用程序退出,只要任务还没有执行完,进程就不会结束。

    private void btnThread_Click(object sender, EventArgs e)
            {
                Thread thread = new Thread(() =>
                {
                    SetMessage("Thread 跨线程访问UI");
                });
                //IsBackground=true表示为后台线程 应用程序退出 哪怕任务没有执行完 也会退出
                //IsBackground=false表示为后台线程 默认为false 应用程序退出 只要任务还没有执行完 进程就不会结束
                thread.IsBackground = true;
                thread.Start();
            }

    代码片段三ThreadPool:ThreadPool是微软为了避免开发人员,无节制的使用线程,而提供的一个线程管理类

    private void btnThreadPool_Click(object sender, EventArgs e)
            {
                //线程池 是微软为了避免开发人员 无节制的使用线程 而提供的一个线程管理类
                ThreadPool.QueueUserWorkItem((obj) =>
                {
                    SetMessage("ThreadPool 跨线程访问UI");
                }, null);
            }

    代码片段四Task:Task有很多的优势,也是后面高版本才推出来的,推荐使用。Task与Thread的区别就是:Task使用的是线程池中的线程,Task较之线程池的优势是:

    1.Task支持取消,完成,失败通知等交互性操作

    2.Task支持线程执行的先后次序

    private void btnTask_Click(object sender, EventArgs e)
            {
                /* Task与Thread的区别就是:Task使用的是线程池中的线程
                 * Task较之线程池的优势是:
                 * 1.Task支持取消,完成,失败通知等交互性操作
                 * 2.Task支持线程执行的先后次序*/
                Task.Factory.StartNew(() =>
                {
                    SetMessage("Task 跨线程访问UI");
                });
    
                Task task = new Task(()=> {
                    SetMessage("Task 跨线程访问UI");
                });
                task.Start();
            }

    代码片段五BackgroundWorker:BackgroundWorker内部是通过线程池实现的,通过事件提供了跨线程访问UI的能力,这个做CS开发,是用的最多的,说白了,太好用。

    //BackgroundWorker 内部是通过线程池实现的
            //BackgroundWorker 通过事件提供了跨线程访问UI的能力
            BackgroundWorker _bgw = new BackgroundWorker();
    
            private void btnBackgroundWorker_Click(object sender, EventArgs e)
            {
                _bgw.WorkerReportsProgress = true;
                _bgw.WorkerSupportsCancellation = true;
                _bgw.DoWork += _bgw_DoWork; ;
                _bgw.ProgressChanged += _bgw_ProgressChanged;
                _bgw.RunWorkerCompleted += _bgw_RunWorkerCompleted;
    
                if (!_bgw.IsBusy)
                {
                    _bgw.RunWorkerAsync();
                }
            }
    
            private void _bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                this.txtMsg.Text = "BackgroundWorker 跨线程访问UI";//注意这里是直接访问UI
            }
    
            private void _bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                this.txtMsg.Text = e.UserState.ToString();//注意这里是直接访问UI
            }
    
            private void _bgw_DoWork(object sender, DoWorkEventArgs e)
            {
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(1000);
                    _bgw.ReportProgress(i, $"{i}秒");
                }
            }

    代码片段六SynchronizationContext:SynchronizationContext同步上下文在通讯中充当传输者的角色,实现功能就是一个线程和另外一个线程的通讯,这个在跨线程一次性要更新很多的UI控件的时候,非常的适用。

    //SynchronizationContext 在通讯中充当传输者的角色,实现功能就是一个线程和另外一个线程的通讯
            SynchronizationContext _syncContext;
    
            private void btnSynchronizationContext_Click(object sender, EventArgs e)
            {
                _syncContext = SynchronizationContext.Current;
                Thread thread = new Thread(() =>
                {
                    if (_syncContext != null)
                    {
                        SendOrPostCallback callBack = (obj) =>
                        {
                            //在某个子线程里 一次性要更新很多UI控件的时候 用这个方法 很nice
                            this.txtMsg.Text = "SynchronizationContext 跨线程访问UI";
                        };
                        _syncContext.Post(callBack, null);//异步
                        //_syncContext.Send(callBack, null);//同步
                    }
                });
                thread.IsBackground = true;
                thread.Start();
            }

    合并之后的代码为:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace ThreadChapter
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void btnThread_Click(object sender, EventArgs e)
            {
                Thread thread = new Thread(() =>
                {
                    SetMessage("Thread 跨线程访问UI");
                });
                //IsBackground=true表示为后台线程 应用程序退出 哪怕任务没有执行完 也会退出
                //IsBackground=false表示为后台线程 默认为false 应用程序退出 只要任务还没有执行完 进程就不会结束
                thread.IsBackground = true;
                thread.Start();
            }
    
            /*
             * WinForm 是通过调用Window API 的GetMessage Or PeekMeeage来处理其他线程发送过来的消息,
             * 这些消息存储在系统的一个消息队列中,创建主界面的线程就是主线程(UI线程),UI线程负责处理该消息队列
             */
    
            private void SetMessage(string message)
            {
                if (this.txtMsg.InvokeRequired)
                {
                    //BeginInvoke or Invoke 负责将消息发送到消息队列
                    this.txtMsg.BeginInvoke(new Action<string>((msg) =>
                    {
                        this.txtMsg.Text = msg;
                    }), message);
                }
                else
                {
                    this.txtMsg.Text = message;
                }
    
            }
    
            private void btnThreadPool_Click(object sender, EventArgs e)
            {
                //线程池 是微软为了避免开发人员 无节制的使用线程 而提供的一个线程管理类
                ThreadPool.QueueUserWorkItem((obj) =>
                {
                    SetMessage("ThreadPool 跨线程访问UI");
                }, null);
            }
    
            private void btnTask_Click(object sender, EventArgs e)
            {
                /* Task与Thread的区别就是:Task使用的是线程池中的线程
                 * Task较之线程池的优势是:
                 * 1.Task支持取消,完成,失败通知等交互性操作
                 * 2.Task支持线程执行的先后次序*/
                Task.Factory.StartNew(() =>
                {
                    SetMessage("Task 跨线程访问UI");
                });
            }
    
    
            //BackgroundWorker 内部是通过线程池实现的
            //BackgroundWorker 通过事件提供了跨线程访问UI的能力
            BackgroundWorker _bgw = new BackgroundWorker();
    
            private void btnBackgroundWorker_Click(object sender, EventArgs e)
            {
                _bgw.WorkerReportsProgress = true;
                _bgw.WorkerSupportsCancellation = true;
                _bgw.DoWork += _bgw_DoWork; ;
                _bgw.ProgressChanged += _bgw_ProgressChanged;
                _bgw.RunWorkerCompleted += _bgw_RunWorkerCompleted;
    
                if (!_bgw.IsBusy)
                {
                    _bgw.RunWorkerAsync();
                }
            }
    
            private void _bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                this.txtMsg.Text = "BackgroundWorker 跨线程访问UI";//注意这里是直接访问UI
            }
    
            private void _bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                this.txtMsg.Text = e.UserState.ToString();//注意这里是直接访问UI
            }
    
            private void _bgw_DoWork(object sender, DoWorkEventArgs e)
            {
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(1000);
                    _bgw.ReportProgress(i, $"{i}秒");
                }
            }
    
            //SynchronizationContext 在通讯中充当传输者的角色,实现功能就是一个线程和另外一个线程的通讯
            SynchronizationContext _syncContext;
    
            private void btnSynchronizationContext_Click(object sender, EventArgs e)
            {
                _syncContext = SynchronizationContext.Current;
                Thread thread = new Thread(() =>
                {
                    if (_syncContext != null)
                    {
                        SendOrPostCallback callBack = (obj) =>
                        {
                            //在某个子线程里 一次性要更新很多UI控件的时候 用这个方法 很nice
                            this.txtMsg.Text = "SynchronizationContext 跨线程访问UI";
                        };
                        _syncContext.Post(callBack, null);//异步
                        //_syncContext.Send(callBack, null);//同步
                    }
                });
                thread.IsBackground = true;
                thread.Start();
            }
        }
    }
  • 相关阅读:
    Android系统根文件系统目录结构
    4面 晶晨半导体 问题总结
    linux 工作队列
    Linux tasklet 的测试
    ArbotiX-M引脚说明
    locobot
    视频地址
    S1雷达ROS包更新指南
    rospy
    mx-28在 labview环境下的开发
  • 原文地址:https://www.cnblogs.com/dwBurning/p/NetThread.html
Copyright © 2020-2023  润新知