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 = ; i < ; i++)
{
Thread.Sleep();
_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 = ; i < ; i++)
{
Thread.Sleep();
_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();
}
}
}
C#多线程,基础知识很重要的更多相关文章
- Java 多线程——基础知识
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- Java多线程基础知识笔记(持续更新)
多线程基础知识笔记 一.线程 1.基本概念 程序(program):是为完成特定任务.用某种语言编写的一组指令的集合.即指一段静态的代码,静态对象. 进程(process):是程序的一次执行过程,或是 ...
- JAVA多线程基础知识(一)
一. 基础知识 要了解多线程首先要知道一些必要的概念,如进程,线程等等.开发多线程的程序有利于充分的利用系统资源(CPU资源),使你的程序执行的更快,响应更及时. 1. 进程,一般是指程序或者任务的执 ...
- Java基础知识强化之多线程笔记01:多线程基础知识(详见Android(java)笔记61~76)
1. 基础知识: Android(java)学习笔记61:多线程程序的引入 ~ Android(java)学习笔记76:多线程-定时器概述和使用
- Java多线程基础知识总结
2016-07-18 15:40:51 Java 多线程基础 1. 线程和进程 1.1 进程的概念 进程是表示资源分配的基本单位,又是调度运行的基本单位.例如,用户运行自己的程序,系统就创建一个进程, ...
- C#中的多线程 - 基础知识
原文:http://www.albahari.com/threading/ 文章来源:http://blog.gkarch.com/threading/part1.html 1简介及概念 C# 支持通 ...
- C#中的多线程 - 基础知识 z
原文:http://www.albahari.com/threading/ 专题:C#中的多线程 1简介及概念Permalink C# 支持通过多线程并行执行代码,线程有其独立的执行路径,能够与其它线 ...
- Java多线程基础知识例子
一.管理 1.创建线程 Thread public class Main { public static void main(String[] args) { MyThread myThread = ...
- Java多线程基础知识篇
这篇是Java多线程基本用法的一个总结. 本篇文章会从一下几个方面来说明Java多线程的基本用法: 如何使用多线程 如何得到多线程的一些信息 如何停止线程 如何暂停线程 线程的一些其他用法 所有的代码 ...
随机推荐
- [luoguP3572] [POI2014]PTA-Little Bird(DP + 单调队列)
传送门 DP方程 f[i] = f[j] + (a[j] <= a[i]) ( i - k < j < i ) 要使 f[i] 最小,需要等号后面的值最小,可以用单调队列来维护. 至 ...
- __asm
来源:http://msdn.microsoft.com/zh-cn/library/45yd4tzz.aspx Microsoft 专用 __asm 关键字调用一个内联汇编,并且可以显示,每当 c. ...
- Codeforces 628D Magic Numbers
题意: 求在[a,b](a,b不含前导0)中的d−magic数中有多少个是m的倍数. 分析: 计数dp Let's call a number d-magic if digit d appears i ...
- JVM(二):Java中的语法糖
JVM(二):Java中的语法糖 上文讲到在语义分析中会对Java中的语法糖进行解糖操作,因此本文就主要讲述一下Java中有哪些语法糖,每个语法糖在解糖过后的原始代码,以及这些语法糖背后的逻辑. 语法 ...
- 爱普生L201
http://tech.sina.com.cn/b/2011-03-29/05481698131.shtml
- MongoDB小结22 - id生成规则
MongoDB的文档必须有一个_id键. 目的是为了确认在集合里的每个文档都能被唯一标识. ObjectId 是 _id 的默认类型. ObjectId 采用12字节的存储空间,每个字节两位16进制数 ...
- 8148和8127中的ezsdk和dvrsdk
http://www.dajudeng.com/d2012081009b8a9d5de87101f69f31952c.html
- HDoj-1250-Hat's Fibonacci-大数
Hat's Fibonacci Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- JS重名解决方案
一个页面如果引用多个JS,或者像ASP.NET MVC,一个视图包含多个子视图,每个子视图有自己的JS,那么变量.函数的重名冲突机会将会大增. 如何解决? 这里有一个方案: 1.用类来封装子页的JS代 ...
- 在java程序中,对于数据的输入/输出操作以“流”(stream)方式进行
在java程序中,对于数据的输入/输出操作以“流”(stream)方式进行