winform程序中使用线程的必要性:

单线程操作在执行耗时任务时会造成界面假死,带来非常差劲的用户体验,有时候甚至会影响到正常的业务执行,使用多线程做相关操作实属不得已之举。

那么在编写程序之前必须要明白的一个点就是窗体的UI的操作只能通过UI线程来执行,其他线程如果要去执行窗体中的控件值修改或者其它【任何和窗体线程相关的操作】,就会报异常,所有人都知道的。为了适应这一特性,于是就有了这样的写法:

  private void button1_Click(object sender, EventArgs e)
{
this.BeginInvoke(new Action(delegate()
{
this.button1.Text = "test";
}));
}

意思很明显就是在本窗体中执行如下代码,说白了就是让括号中的代码在UI线程中执行,如果只是执行一个很简单的任务不会有任何问题,因为时间够快,给人的感觉好像窗体并没有因为这样写就假死。现在把代码改成如下这样:

  private void button1_Click(object sender, EventArgs e)
{
this.BeginInvoke(new Action(delegate()
{
for (int i = 0; i < 100; i++)
{
this.button1.Text = i.ToString();
Thread.Sleep(1000);
}
}));
}

预期的执行结果应该是,每隔一秒按钮上边的文本就会自加1直到100,但结果并不是这样,当点击按钮之后,窗体会进入假死状态,点击不会有任何响应。这篇文章就是要解决这样的问题。主要也是做一个简单的总结,备用。

要处理这样的问题最简单粗暴的方式是这样直接忽略掉其他线程不可以执行UI。代码非常简单,只需要在界面初始化中添加如下代码就可以,

 public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;//时候捕获对错误线程的调用... 忽略掉自然就可以在其他线程中去访问窗体线程了。
} private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(() =>
{
this.button1.Text = "测试";
});
t.Start();
}

这种方式确实已经做到可以在不同线程中去操作窗体线程,但并没有什么卵用,遇到上边的那种情况每隔一秒让按钮的数字自增一,依然无法做到。

请回头看标颜色的那句话。

然后再看看Invoke,BeginInvoke到底是什么东西:

直接F12找到签名对应的解释

//
// 摘要:
// 在创建控件的基础句柄所在线程上异步执行指定委托。
//
// 参数:
// method:
// 对不带参数的方法的委托。
//
// 返回结果:
// 一个表示 System.Windows.Forms.Control.BeginInvoke(System.Delegate) 操作的结果的 System.IAsyncResult。
//
// 异常:
// System.InvalidOperationException:
// 找不到适当的窗口句柄。
[EditorBrowsable(EditorBrowsableState.Advanced)]
public IAsyncResult BeginInvoke(Delegate method);
 //
// 摘要:
// 在拥有此控件的基础窗口句柄的线程上执行指定的委托。
//
// 参数:
// method:
// 包含要在控件的线程上下文中调用的方法的委托。
//
// 返回结果:
// 正在被调用的委托的返回值,或者如果委托没有返回值,则为 null。
public object Invoke(Delegate method);

关键字: 拥有此控件的基础窗口句柄的线程上执行执行的委托。同步异步的区别

this.BeginInvoke(new Action(delegate()

            {
for (int i = 0; i < 100; i++)
{
this.button1.Text = i.ToString();
Thread.Sleep(1000);
}
}));

那么在这里的意思就是在窗体线程中执行button.text=i.tostring,然后让窗体线程休眠1000毫秒,窗体休眠了,自然而然就不会对你的操作做出响应,不管是不是异步都是在窗体线程中执行的,显而易见问题是出在这里的,那么既然知道了问题所在。解决的办法也非常简单,那就是,

让所有和窗体操作无关的任务不要在窗体线程中执行,所有和窗体相关操作的动作全部放到窗体线程中去执行,大家各行其道,问题就自然解决了。刚刚的按钮文本每秒加1,就可以用下边的这种方式来写:

 private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(() =>
{
for (int i = ; i < ; i++)
{
Thread.Sleep();
this.button1.Invoke(new Action(delegate()
{
this.button1.Text = i.ToString();
}));
}
});
t.Start();
}

没错,就是这样,新开一个线程,让所有的操作都在线程中执行,其中如果有涉及到对窗体的操作转会到窗体线程执行对应操作,或者像这样

 public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;//忽略其他线程执行UI的错误
} private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(() =>
{
for (int i = ; i < ; i++)
{
Thread.Sleep();
this.button1.Text = i.ToString();
}
});
t.Start();
}

这种方式明显是有点取巧,而且在一定情况下会造成窗体闪烁,可能会不稳定,比如多个线程同时执行一个按钮的text显示,但至少这种方式写起来没那么麻烦。至于如何取舍就具体问题具体分析处理了。

 

winform开发 总结1>winform程序使用线程的必要性,以及正确的使用方式的更多相关文章

  1. 在Winform开发中,我们使用的几种下拉列表展示字典数据的方式

    在Winform开发中中,我们为了方便客户选择,往往使用系统的字典数据选择,毕竟选择总比输入来的快捷.统一,一般我们都会简单封装一下,以便方便对控件的字典值进行展示处理,本篇随笔介绍DevExpres ...

  2. WinForm开发(6)——C#/winform程序打包部署时,如何把SQL数据库一起打包进去

    打包数据库到安装程序中 方法1. 备份/恢复先备份数据库:backup database 数据库 to disk='c:\备份.bak' 将备份文件打包到安装程序中. 在第一次运行程序的时候,进行数据 ...

  3. Winform开发的快速、健壮、解耦的几点建议

    在Winform开发领域开发过十多年的项目中,见证着形形色色的架构和官方技术的应用,从最早类似Winform模式的WebForm技术,到接着的JQuery+界面组件,再到Asp.net Core的技术 ...

  4. Winform开发中的困境及解决方案

    在我们开发各种应用的时候,都会碰到很多不同的问题,这些问题涉及架构.模块组合.界面处理.共同部分抽象等方面,我们这里以Winform开发为例,从系统模块化.界面组件选择.业务模块场景划分.界面基类和辅 ...

  5. C#+Winform开发窗体程序

    学习笔记 第一章:winform基础 一.概述 1.Windows Form(简称WinForm) 是微软.NET平台下用于开发"图形界面"应用程序的组件. 2.C/S架构 客户机 ...

  6. .NET混合开发解决方案8 WinForm程序中通过设置固定版本运行时的BrowserExecutableFolder属性集成WebView2控件

    系列目录     [已更新最新开发文章,点击查看详细] 在我的博客<.NET混合开发解决方案7 WinForm程序中通过NuGet管理器引用集成WebView2控件>中介绍了WinForm ...

  7. Java进击C#——应用开发之WinForm开发

    本章简言 上一章笔者介绍了关于WinForm环境.这一章笔者将继续讲WinForm.只不过更加的面向开发了.事实就是在学习工具箱里面的控件.对于WinForm开发来讲,企业对他的要求并没有那么高.但是 ...

  8. WPF与WinForm开发有什么区别?

    转自http://hi.baidu.com/leoliu83/blog/item/1d1a4a66dcb41134aa184cfd.html WPF开发于WinForm之后,从技术发展的角度,WPF比 ...

  9. C# WinForm开发系列 - 文章索引

    该系列主要整理收集在使用C#开发WinForm应用文章及相关代码, 平时看到大家主要使用C#来开发Asp.Net应用,这方面的文章也特别多,而关于WinForm的文章相对少很多,而自己对WinForm ...

随机推荐

  1. BFC布局

    这几天都没有写博客,自己的懒惰又要跑出来了,发觉不能再这样下去了,不然就什么都不想干了,然后将之前已经写得差不多的博客重新检视了一遍.这篇博客已经写得挺久的了,但是一直没有发布,现在补充了一些,也让自 ...

  2. ES6之解构赋值

    截止到ES6,共有6种声明变量的方法,分别是var .function以及新增的let.const.import和class: 我们通常的赋值方法是: var foo='foo'; function ...

  3. 沙盒解决方案解决SharePoint 2013 以其他身份登陆的问题

    众所周知,SharePoint 2013没有像SharePoint 2010那样有一个叫"以其他身份登录"的菜单项. 当然解决方案也很多,比如你可以直接修改Welcome.ascx ...

  4. 3种方法快速制作tpk文件 [转]

    tpk是ArcGIS10.1推出的一种新的数据文件类型,主要是用于将切片文件打包形成离线地图包,tpk可以在ArcGIS Runtime或者ArcGIS for Android/iOS中作为切片底图被 ...

  5. Dynamics CRM 2015-超大Solution导入问题

    我们在将比较大的solution导入CRM的时候,经常会遇到超时的问题,这是因为CRM的本身的优化限制导致的,那么如何解决呢? 官方已经有了解决方案了. 在浏览完两种解决方法之后,我们要知道的是: 1 ...

  6. 消费RabbitMQ时的注意事项,如何禁止大量的消息涌到Consumer

    按照官网提供的订阅型写法( Retrieving Messages By Subscription ("push API")) 我发现,RabbitMQ服务器会在短时间内发送大量的 ...

  7. webform(八)——LinQ简单增、删、改、查

    一.简单介绍 1.LinQ to Sql类(NET Language Integrated Query (LINQ) ) LINQ定义了大约40个查询操作符,如select.from.in.where ...

  8. 使用python的Flask实现一个RESTful API服务器端[翻译]

    最近这些年,REST已经成为web services和APIs的标准架构,很多APP的架构基本上是使用RESTful的形式了. 本文将会使用python的Flask框架轻松实现一个RESTful的服务 ...

  9. android 利用Handler触发另一个activity方法

    如activityA代码: activityB = new ActivtyB(mHandler,CLOSE_SEARCH_MSG);//新建对像B传递,一个handler和Message,然后在act ...

  10. [No0000A4]DOS命令(cmd)批处理:替换字符串、截取字符串、扩充字符串、获取字符串长度

    1.替换字符串,即将某一字符串中的特定字符或字符串替换为给定的字符串.举例说明其功能:========================================= @echo off set a ...