在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
本文通过介绍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(); } private async void btnTask_Click(object sender, EventArgs e) { //还可以使用async/await实现异步 string msg = await DoSomething(); this.txtMsg.Text = msg; } private async Task<string> DoSomething() { return await Task.Run(() => { System.Threading.Thread.Sleep(50 * 1000); return "async异步方法"; }); } 代码片段五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(); } } } |
请发表评论