C# winform多线程的小例子

在文本框中输入一个数字,点击开始累加按钮,程序计算从1开始累计到该数字的结果。因为该累加过程比较耗时,如果直接在UI线程中进行,那么当前窗口将出现假死。为了有更好的用户体验,程序启动一个新的线程来单独执行该计算,然后每隔200毫秒读取一次累加结果,并把结果显示到文本框下方的label控件中。同时,程序支持取消操作,点击取消累计按钮,程序将取消累加操作,并把当前累加值显示到label中。为了方便后面的描述,我把UI线程称作主线程,把执行累加计算的线程称作工作者线程。该过程有两个关键点:
1:如何在工作者线程中访问主线程创建的控件;
2:如何取消比较耗时的计算;
为了便于在工作者线程中调用累加过程,我把它写成一个单独方法,如下:
/// <summary>
/// 从1累加到指定的值,为了让该方法支持取消操作所以需要CancellationToken参数
/// </summary>
/// <param name="countTo">累加到的指定值</param>
/// <param name="ct">取消凭证</param>
private void CountTo(int countTo, CancellationToken ct)
{
int sum = ;
for (; countTo > ; countTo--)
{
if (ct.IsCancellationRequested) {
break;
}
sum += countTo;
//Invoke方法用于获得创建lbl_Status的线程所在的上下文
this.Invoke(new Action(()=>lbl_Status.Text = sum.ToString()));
Thread.Sleep();
}
}
该方法就是用于累加数字,它有两个需要注意的地方
1:方法需要传递一个CancellationToken参数,用于支持取消操作(《clr via c# 3版》中把这种方式称作协作式取消,也就是说某一个操作必须支持取消,然后才能取消该操作);
2:为了允许工作者线程访问主线程创建的lbl_Status控件,我在该线程中使用this.Invoke方法。该方法用于获得主线程所创建控件的访问权。它需要一个委托作为参数,在该委托中我们可以定义对lbl_Status的操作。例如在上例中我就是把当前的累加结果赋给lbl_Status的Text属性。
然后我们看一下如何在一个共走着线程中执行计算耗时的操作,也就是“开始累加”按钮的操作:
private void btn_Count_Click(object sender, EventArgs e)
{
_cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(state=>CountTo(int.Parse(txt_CountTo.Text)));
}
我使用线程池线程来执行该操作,之所以使用线程池线程而不是自己的Threading对象,是因为线程池是由.NET FrameWork进行维护,默认已经为我们创建好了一些线程,从而省去创建新线程造成的一些列资源消耗,同时,完成计算任务后该线程池线程会自动回到池中等待下一个任务。我把_cts作为一个成员变量,声明如下:
private CancellationTokenSource _cts;
它需要引入 System.Threading 命名空间。
取消操作更加简单,代码如下:
private void btn_Cancel_Click(object sender, EventArgs e)
{
if (_cts != null)
_cts.Cancel();
}
这样我们就完成了在winform中使用多线程的例子,同时该例子支持取消操作。完整代码如下:
using System;
using System.Threading;
using System.Windows.Forms;
namespace WinformApp
{
public partial class Form1 : Form
{
private CancellationTokenSource _cts;
public Form1()
{
InitializeComponent();
}
/// <summary>
/// 从1累加到指定的值,为了让该方法支持取消操作所以需要CancellationToken参数
/// </summary>
/// <param name="countTo">累加到的指定值</param>
/// <param name="ct">取消凭证</param>
private void CountTo(int countTo) {
int sum = ;
for (; countTo > ; countTo--) {
if (ct.IsCancellationRequested) {
break;
}
sum += countTo;
//Invoke方法用于获得创建lbl_Status的线程所在的上下文
this.Invoke(new Action(()=>lbl_Status.Text = sum.ToString())); Thread.Sleep();
}
}
private void btn_Count_Click(object sender, EventArgs e)
{
_cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(state=>CountTo(int.Parse(txt_CountTo.Text)));
}
private void btn_Cancel_Click(object sender, EventArgs e)
{
if (_cts != null)
_cts.Cancel();
}
private void btn_Pause_Click(object sender, EventArgs e)
{
}
}
}
解决跨线程访问的问题
主要有两个方案:
1、关闭跨线程检查。
2、通过委托的方式,在控件的线程上执行。
具体的代码如下:
using System;
using System.Threading;
using System.Windows.Forms; namespace WindowsFormsApplication1
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
//方法一:不进行跨线程安全检查
CheckForIllegalCrossThreadCalls = false;
} private void button1_Click(object sender, EventArgs e)
{
Thread th1 = new Thread(new ThreadStart(CalNum));
th1.Start();
} private void CalNum()
{
SetCalResult(DateTime.Now.Second);
} //方法二:检查是否跨线程,然后将方法加入委托,调用委托
public delegate void SetTextHandler(int result);
private void SetCalResult(int result)
{
if (label2.InvokeRequired == true)
{
SetTextHandler set = new SetTextHandler(SetCalResult);//委托的方法参数应和SetCalResult一致
label2.Invoke(set, new object[] { result }); //此方法第二参数用于传入方法,代替形参result
}
else
{
label2.Text = result.ToString();
}
}
}
}
改进
在我的Winform程序中,子线程涉及到对多个控件的更改,于是封装了一下,我这里使用的是拓展方法,只有在.net 3.5上才能支持,如果是.net2.0的环境,需要添加
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)]
public class ExtensionAttribute : Attribute { }
}
封装如下:
using System.Threading;
using System.Windows.Forms; namespace WindowsFormsApplication1
{
public static class Class1
{
/// <summary>
/// 跨线程访问控件 在控件上执行委托
/// </summary>
/// <param name="control">控件</param>
/// <param name="method">执行的委托</param>
public static void CrossThreadCalls(this Control control, ThreadStart method)
{
if (!control.IsHandleCreated || control.IsDisposed || control.Disposing)
{
return;
} if (method == null)
{
return;
} if (control.InvokeRequired)
{
control.Invoke(method, null);
}
else
{
method();
}
}
}
}
线程池是不可控制的.
C# winform多线程的小例子的更多相关文章
- c# BackGroundWorker 多线程操作的小例子
在我们的程序中,经常会有一些耗时较长的运算,为了保证用户体验,不引起界面不响应,我们一般会采用多线程操作,让耗时操作在后台完成,完成后再进行处理或给出提示,在运行中,也会时时去刷新界面上的进度条等显示 ...
- c# BackGroundWorker 多线程操作的小例子 (转)
在我们的程序中,经常会有一些耗时较长的运算,为了保证用户体验,不引起界面不响应,我们一般会采用多线程操作,让耗时操作在后台完成,完成后再进行处理或给出提示,在运行中,也会时时去刷新界面上的进度条等显示 ...
- Java多线程分批发送消息的小例子
需求: 假设有10万个用户,现在节假日做活动,需要给每个用户发送一条活动短信,为了提高程序的效率,建议使用多线程分批发送. 这里值得注意的是: 每开一个线程都会占用CPU的资源,所以线程根据所需要的条 ...
- WinForm多线程学习文档
基础篇 怎样创建一个线程 受托管的线程与 Windows线程 前台线程与后台线程 名为BeginXXX和EndXXX的方法是做什么用的 异步和多线程有什么关联 WinForm多线程编程篇 我的多线程W ...
- WinForm多线程实现HTTP网络检测工具
一.背景描述与课程介绍 明人不说暗话,跟着阿笨一起玩WinForm.本次分享课程属于<C#高级编程实战技能开发宝典课程系列>中的一部分,阿笨后续会计划将实际项目中的一些比较实用的关于C#高 ...
- WebService小例子———
WebService学习(刚开始) ———————————————————————————————————————————————————————————————————— WebService:跨平 ...
- c#winform多线程感想
我很菜所以好好学!!! 最近在做一个关于识别的项目,手动识别和自动识别,为了更好的保证自动识别不会引起界面的卡顿等现象,所以简单的学习了一下多线程,也只是入门但还是记录一下. 一.首先了解一下用多线程 ...
- 关键字Lock的简单小例子
一.什么是Lock? Lock——字面上理解就是锁上:锁住:把……锁起来的意思: 为什么要锁?要锁干什么?——回到现实中可想象到,这个卫生间我要上,其他人不要进来!(所以我要锁住门):又或者土味情话所 ...
- springmvc入门的第一个小例子
今天我们探讨一下springmvc,由于是初学,所以简单的了解一下 springmvc的流程,后续会持续更新... 由一个小例子来简单的了解一下 springmvc springmvc是spring框 ...
随机推荐
- Python基础之生成器
1.生成器简介 首先请确信,生成器就是一种迭代器.生成器拥有next方法并且行为与迭代器完全相同,这意味着生成器也可以用于Python的for循环中.另外,对于生成器的特殊语法支持使得编写一个生成器比 ...
- 烂泥:高负载均衡学习haproxy之安装与配置
本文由秀依林枫提供友情赞助,首发于烂泥行天下 有关高负载均衡的软件,目前使用比较多的是haproxy.nginx和lvs.下面我们就开始学习haprxoy这款软件. 一.haproxy介绍 以下开始介 ...
- Session Sticky About Nginx
Nginx以前对session 保持支持不太好,主要采用ip_hash把同一来源的客户(同一C段的IP)固定指向后端的同一台机器,ip_hash有个缺点是不能实现很好的负载均衡:直到nginx的扩展模 ...
- Team Foundation Server 15 功能初探
1. 系统安装 1.1. 系统需求 新版的TFS的系统要求发生了很大的变化,主要包含: - 不再支持32位的操作系统,只支持64位操作系统 - 只支持SQL 2014和SQL Server 2016, ...
- KVM 介绍(4):I/O 设备直接分配和 SR-IOV [KVM PCI/PCIe Pass-Through SR-IOV]
学习 KVM 的系列文章: (1)介绍和安装 (2)CPU 和 内存虚拟化 (3)I/O QEMU 全虚拟化和准虚拟化(Para-virtulizaiton) (4)I/O PCI/PCIe设备直接分 ...
- C#基础--基于POP3协议的邮件接收和基于STMP的邮件发送
最近在用outlook同步邮件.对邮件协议有一点兴趣.于是就去收集了一些资料,学习了一下如何通过.net来实现邮件的收发. 一:SMTP协议 1.什么是SMTP协议: SMTP目前 ...
- Struts2中的Unable to load configuration错误的分析与解决方法
当我们遇到 Unable to load configuration. 这样的错误时,可以根据具体的错误提示找出错误的原因. Unable to load configuration. - inter ...
- UI的重用性
UI抽取思路 一款手机游戏中UI有几十个到上百个不等,如果一个一个做这些UI,无疑会花费很多时间. 近期我们的游戏UI已经是第N次改版了,经过这N多次的修改,我总结了UI其实有很多的共性(就是相同性) ...
- IT菜鸟的第2天(输入输出,数据类型,运算符的使用)
1:输入输出 另一种读写方法: 注释:Console.Write(Line{自动换行})是输入,string xxx = Console.ReadLine();是输出. string :字符串类型 ...
- u3d_shader_surface_shader_2
http://docs.unity3d.com/Manual/SL-SurfaceShaderExamples.html http://my.oschina.net/u/138823/blog/181 ...