浅谈C#中Invoke与BeginInvoke在主副多线程如何按顺序执行中的执行顺序和区别

温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
{list postlist as x}
{if !!x.newAbstract && !!x.newAbstract.imageUrl}
{/if} {if !!x.newAbstract && !!x.newAbstract.blogAbstract} ${x.newAbstract.blogAbstract} {/if}
{if defined('newslist')&&newslist.length>0} {list newslist as x} {if x_index>7}{break}{/if}
{/list} {/if}
this.p={b:2,totalBlogCount:146,friendstatus:'none',followstatus:'unFollow'};
& & & & & &
网易公司版权所有&&
汇聚1000万达人的兴趣社区下载即送20张免费照片冲印
扫码下载App
{list x.l as y}
{/list} {/list}
{if defined('wl')} {list wl as x}{/list} {/if}1487人阅读
在以前的章节中,我们不只一次的提到过,不能在非创建控件的线程中操作元素,否则会和控件创建线程(一般是主线程)产生冲突,造成不可预料的后果。
该如何解决这个问题呢?除了上一节所讲的和以外,微软将Control类实现了接口,提供了和方法来提供让其它线程更新界面控件的机制。
下边还是通过一个例子给大家讲解一下和
首先新建一个应用程序,在窗体上做如下布局:
然后,新建一个委托
public&delegate&void&ShowTime();
在。()按钮的事件里添加如下代码:
Button_click(object&sender&,EventArgs&e)
&&&&ShowTime&showTime&=&new&ShowTime(Time);
&&&&&&&&&&&&this.Invoke(showTime);//Invoke()方法需要一个委托参数,也就是要执行委托方法
&&&&&&&&&&&&//在执行委托之后弹出对话框,显示当前的线程,是否为后台线程
&&&&&&&&&&&&MessageBox.Show(&当前线程为:是否为后台线程?
//委托调用的方法
private&&void&Time()
&&&&&&&&&&&&this.button10.Text&=&DateTime.Now.ToString();//将属性设置为当前的时间
&&&&&&&&&&&&for&(int&i&=&0;&i&&=&100;&i++)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&sum&+=&i;
&&&&&&&&&&&&}
&&&&&&&&&&&&MessageBox.Show(&Sum值为:当前线程为:是否为后台线程?显示当前的线程,是否为后台线程
实验结果如下:依次是
由实验结果我们可以得出以下结论:方法所执行的委托方法跟创建元素的线程是一个线程,也就是主线程。虽然执行了委托方法,但是并没有创建新的线程,而且从实验结果出现的先后顺序可以得知,是同步独占式的执行,只有在方法执行完以后才执行方法里的方法。
下面在看下
首先新建一个委托:
public&delegate&void&ShowTime(int&a);
然后在()方法中添加如下代码:
private&void&button11_Click(object&sender,&EventArgs&e)
&&&&&&&&&&&&ShowTime&showTime&=&new&ShowTime(Time);//新建一个委托实例
&&&&&&&&&&&&this.BeginInvoke(showTime,&100);//调用方法,第一个参数是一个委托实例,第二参数是委托调用函数所需要的参数
&&&&&&&&&&&&MessageBox.Show(&当前线程为:是否为后台线程?显示当前的线程
//委托调用函数
private&&void&Time(int&a)
&&&&&&&&&&&&this.button11.Text&=&DateTime.Now.ToString();//改变的属性为当前的时间
&&&&&&&&&&&&for&(int&i&=&0;&i&&=&a;&i++)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&sum&+=&i;
&&&&&&&&&&&&}
&&&&&&&&&&&&Thread.Sleep(3000);
&&&&&&&&&&&&MessageBox.Show(&Sum值为:当前线程为:是否为后台线程?显示当前的线程的
实验结果如下:依次是:
由实验结果我们可以得出以下结论:方法所执行的委托方法跟创建元素的线程是一个线程,也就是主线程。虽然执行了委托方法,但是并没有创建新的线程,而且从实验结果出现的先后顺序可以得知,在这里也是同步执行的,只有在方法执行完以后才执行方法里的方法。之前有看网上、园子里说是异步执行的这个本没有错。但是所谓的异步在本例中相当于执行,);语句之后,应该马上执行下边一句最后再执行委托方法,为了对此进行测试,我在()函数里将线程休眠了秒钟,但是事实证明,只有在()方法执行完之后,才会执行语句,才会出现上边所示的结果,所谓的异步执行,在这里并没有得到体现,这是为什么呢?让我们继续往下看。
由以上的例子可知Control的和的委托方法是在主线程,即线程上执行的。如果委托方法不会花费很长的时间,那直接用和方法,如果你的委托方法用来取花费时间长的数据,然后更新界面什么的,千万别在线程上调用和,因为这些是依然阻塞线程的,造成界面的假死,那么我们该该如何办呢?最好的解决办法就是重新建立一个线程,然后在线程调用函数里再启用或者
下面还是给大家做一个示例,然后在示例中慢慢讲解。
在窗体里放置个如图
先创建一个委托&
然后在调用按钮的方法中添加如下代码:
private&void&button4_Click(object&sender,&EventArgs&e)
&&&&&&&&&&&&//显示当前线程ID
&&&&&&&&&&&&MessageBox.Show(&A,我最先执行!
&&&&&&&&&//创建一个新的线程,并在线程调用函数中,调用方法
      
&&&&&&&&&&&&thread.Start();
&&&&&&&&&&&&MessageBox.Show(&B,我和同步执行!
//线程调用方法,在这个方法里再调用方法操作元素
void&ShowThread()
&&&&&&&&&&&&MessageBox.Show(&C,Thread调用方法开始
&&&&&&&&&&&&this.Invoke(new&Showing(ShowResult));
&&&&&&&&&&&&MessageBox.Show(&E,我最后执行!调用方法结束!
&&&&&&&&//委托调用的函数
&&&&&&&&private&static&&void&ShowResult()
&&&&&&&&&&&&int&sum&=&0;
&&&&&&&&&&&&for&(int&i&=&0;&i&&&;&i++)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&sum&+=&i;
&&&&&&&&&&&&}
&&&&&&&&&&&&MessageBox.Show(&D:占用线程。显示当前线程所在
实验结果如下:
由实验结果可知,在线程调用的方法里,启动了方法,在方法调用的委托方法完全执行完之后,才执行里的,我最后执行!调用方法结束!由此可见方法是同步的。
下面把方法也放在一个新的线程调用函数中执行,代码基本上不用怎么变,只需要把调用按钮的方法里的代码复制到调用按钮的方法里就行,然后把的代码做如下改动:
&&&&&&&&&&&&MessageBox.Show(&C,Thread调用方法开始
&&&&&&&&&&&&this.BeginInvoke(new&Showing(ShowResult));
&&&&&&&&&&&&MessageBox.Show(&E,我最后执行!调用方法结束!
运行结果如下:、
由实验结果可知:在线程调用的方法里,启动了方法,方法调用的委托方法之后,马上就执行了里的,我最后执行!调用方法结束!,并没有等待委托方法执行完,由于执行时间较长,故最后才显示消息框,所以说这里的是异步操作。这样就可以与是同步操作明显的区分开了。
说到和还有朋友问的、和、的区别,之前的章节中有介绍过的委托的主要用于异步操作,其本质是在线程池里又启用了一个新的后台线程,而它本身就是在创建控件的线程里(也就是)线程里执行的,它主要依附于具体的元素,可以直接的访问元素,但是通过委托的是绝对不能访问元素的,这点已经强调了很多次了。
好了,关于《线程实用详解》这一系列的文章到这里已经结束了,通过学习、总结,自己也收获了很多的东西,当然,还有有些地方总结的还不够全面,还需要继续的努力。学习就是这样一个过程,通过不断的练习、反思、总结然后再反思、练习、总结、升华。希望能给大家带来些帮助,也还请大家不吝赐教,共同交流探讨。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:198859次
积分:2870
积分:2870
排名:第5211名
原创:113篇
评论:118条
(1)(1)(1)(1)(2)(1)(3)(3)(1)(1)(6)(3)(5)(12)(3)(11)(2)(3)(9)(29)(12)(2)(2)(1)浅谈C#中Invoke与BeginInvoke在主副线程中的执行顺序和区别
今天无意中看到有关Invoke和BeginInvoke的一些资料,不太清楚它们之间的区别。所以花了点时间研究了下。
  据msdn中介绍,它们最大的区别就是BeginInvoke属于异步执行的。
Control.Invoke 方法 (Delegate) :在拥有此控件的基础窗口句柄的线程上执行指定的委托。
Control.BeginInvoke 方法 (Delegate) :在创建控件的基础句柄所在线程上异步执行指定委托。
  于是用下面的代码进行初步的测试:
  1.主线程调用Invoke   
/// &summary&
/// 直接调用Invoke
/// &/summary&
private void TestInvoke()
listBox1.Items.Add(&--begin--&);
listBox1.Invoke(new Action(() =&
listBox1.Items.Add(&Invoke&);
<span style="color: #
<span style="color: #
<span style="color: #
Thread.Sleep(<span style="color: #00);
<span style="color: #
listBox1.Items.Add(&--end--&);
<span style="color: #
输出:    
  从输出结果上可以看出,Invoke被调用后,是马上执行的。这点很好理解。
  2.主线程调用BeginInvoke
/// &summary&
/// 直接调用BeginInvoke
/// &/summary&
private void TestBeginInvoke()
listBox1.Items.Add(&--begin--&);
var bi = listBox1.BeginInvoke(new Action(() =&
//Thread.Sleep(10000);
<span style="color: #
listBox1.Items.Add(&BeginInvoke&);
<span style="color: #
<span style="color: #
Thread.Sleep(<span style="color: #00);
<span style="color: #
listBox1.Items.Add(&--end--&);
<span style="color: #
输出:  
  从输出能看出,只有当调用BeginInvoke的线程结束后,才执行它的内容。
  不过有两种情况下,它会马上执行:
  使用EndInvoke,检索由传递的
表示的异步操作的返回值。
/// &summary&
/// 调用BeginInvoke、EndInvoke
/// &/summary&
private void TestBeginInvokeEndInvoke()
listBox1.Items.Add(&--begin--&);
var bi = listBox1.BeginInvoke(new Action(() =&
Thread.Sleep(<span style="color: #00);
listBox1.Items.Add(&BeginInvokeEndInvoke&);
listBox1.EndInvoke(bi);
listBox1.Items.Add(&--end--&);
输出:  
  同一个控件调用Invoke时,会马上执行先前的BeginInvoke
/// &summary&
/// 调用BeginInvoke、Invoke
/// &/summary&
private void TestBeginInvokeInvoke()
listBox1.Items.Add(&--begin--&);
listBox1.BeginInvoke(new Action(() =&
Thread.Sleep(<span style="color: #00);
listBox1.Items.Add(&BeginInvoke&);
listBox1.Invoke(new Action(() =&
listBox1.Items.Add(&Invoke&);
listBox1.Items.Add(&--end--&);
  注:在主线程中直接调用Invoke、BeginInvoke、EndInvoke都会造成阻塞。所以应该利用副线程(支线线程)调用。
  3.支线线程调用Invoke
  创建一个线程,并在线程中调用Invoke,同时测试程序是在哪个线程中调用Invoke。&
/// &summary&
/// 线程调用Invoke
/// &/summary&
private void ThreadInvoke()
listBox1.Items.Add(&--begin--&);
new Thread(() =&
Thread.CurrentThread.Name = &ThreadInvoke&;
<span style="color: #
listBox1.Invoke(new Action(() =&
<span style="color: #
<span style="color: #
Thread.Sleep(<span style="color: #000);
<span style="color: #
this.listBox1.Items.Add(&ThreadInvoke:& + Thread.CurrentThread.Name);
<span style="color: #
<span style="color: #
}).Start();
<span style="color: #
Thread.Sleep(<span style="color: #00);
<span style="color: #
listBox1.Items.Add(&--end--&);
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
private void button1_Click(object sender, EventArgs e)
<span style="color: #
<span style="color: #
Thread.CurrentThread.Name = &Main&;
<span style="color: #
ThreadInvoke();
<span style="color: #
<span style="color: #
<span style="color: #
private void button2_Click(object sender, EventArgs e)
<span style="color: #
<span style="color: #
listBox1.Items.Add(&button2_Click&);
<span style="color: #
  当点击button1后,我试图去点击button2,却发现程序被阻塞了。可见Invoke尽管在支线程中调用,实际上仍然在拥有此控件的基础窗口句柄的线程上执行。
  接着来测试下在支线程中调用BeginInvoke.
  4.支线线程调用BeginInvoke
/// &summary&
/// 线程调用BeginInvoke
/// &/summary&
private void ThreadBeginInvoke()
listBox1.Items.Add(&--begin--&);
new Thread(() =&
Thread.CurrentThread.Name = &ThreadBeginInvoke&;
<span style="color: #
Thread.Sleep(<span style="color: #000);
<span style="color: #
string temp = &Before!&;
<span style="color: #
listBox1.BeginInvoke(new Action(() =&
<span style="color: #
<span style="color: #
this.listBox1.Items.Add(temp + &:& + Thread.CurrentThread.Name);
<span style="color: #
<span style="color: #
temp += &After!&;
<span style="color: #
}).Start();
<span style="color: #
Thread.Sleep(<span style="color: #00);
<span style="color: #
listBox1.Items.Add(&--end--&);
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
private void button1_Click(object sender, EventArgs e)
<span style="color: #
<span style="color: #
Thread.CurrentThread.Name = &Main&;
<span style="color: #
ThreadBeginInvoke();
<span style="color: #
<span style="color: #
<span style="color: #
private void button2_Click(object sender, EventArgs e)
<span style="color: #
<span style="color: #
listBox1.Items.Add(&button2_Click&);
<span style="color: #
输出:  
  从这结果中我们能得出以下几点:
线程真正开始执行,是在创建它的线程结束后执行。
真正执行BeginInvoke的线程就是创建控件线程上。
BeginInvoke在线程中调用时,是异步执行的而且没有对主线程造成阻塞。(button2_Click在Before!After!:Main前面)
BeginInvoke只有当创建它的线程结束后执行。(当然你也能通过EndInvoke、Invoke让其提早执行。)
总结:  
  以下为了方便理解,假设如下:
    主线程表示Control.Invoke或Control.BeginInvoke中Control所在的线程,即创建该创建的线程。(一般为UI线程)
    支线程表示调用Invoke或BeginInvoke的线程。
Invoke、BeginInvoke始终在主线程中执行。
Invoke被调用时就会直接执行,也就是直接阻塞线程(包括主支线程),直到它结束。而BeginInvoke只有等支线程结束或者调用EndInvoke、Invoke时才会开始执行。
Invoke不管在哪里执行都会造成主线程的阻塞。而BeginInvoke只会阻塞支线程,而对于主线程是异步执行。(注意,如果在主线程中调用,也会阻塞主线程)。
在支线程中,应该使用BeginInvoke,否则调用Invoke将导致支线程阻塞主线程,该支线程就没有存在的意义。(当然有特殊需求除外)
本站推荐文章:
一、前言 (1)对于Thread的Abort方法,如果线程当前正在执行的是一段非托管代码,那么C...
该Demo主要涉及获取文件名、目录、扩展名,以及两个字符合并为路径,CSDN的关于获取指...
本文主要介绍可以在C#中使用的1D/2D编码解码器。条形码的应用已经非常普遍,几乎所有...
一. 测试原理 字符串相关度计算是文本处理和数据挖掘中一个不可或缺的方法,例如论文...
应网友之邀为大家提供一下WebQQ的MD5加密算法,因为MD5是WebQQ模拟登录过程中最难的部...
将C#图像库的基础部分开源了( /xiaotie/GebImage )。这个库比较...
在这之前,我必须要声明的是,代码仅供学习参考使用,以此代码发生的后果,请自己负责...
先看看工具界面: 登录界面: 主界面: 主要代码: 1. 把 excel 文件读到 DataTable /...
开始进入工业自动化,买的工控机带有GPIO接口,可用于直接控制继电器。 从厂家拿到接...
c#共享内存操作相对c++共享内存操作来说原理是一样,但是c#会显得有点复杂。 现把昨天...
本站热点文章:
关键词: 委托 线程 异步操作 大数据存储过程分页 实时刷新界面数据 声音报警 任务栏...
前提:引入COM组件 1、创建 object oMissing = System.Reflection.Missing.V Wor...
先看看工具界面: 登录界面: 主界面: 主要代码: 1. 把 excel 文件读到 DataTable /...
下面贴出自己用C#写的注册表操作类,欢迎大家拍砖! 1.注册表基项静态域 1 /// summar...
趁空闲的时间,对键盘钩子进行了学习,通过C#这门语言来设计和实现:下面是我设计的类...
一、WebService在cs后台程序中的调用 A、通过命名空间和类名直接调用 示例: WebServi...
这些技巧不好找,我归类总结了一下,对大家访问使用也方便,好了,列表如下: 1.怎样...
摘 要: 在《 csdn 开发高手》 2004 年第 03 期中的《化功大法将 DLL 嵌入 EXE 》一文...
腾讯向大众提供了申请QQ的界面,方便很多潜在用户加入QQ大军中,注册页面是http://reg...
#region 读取Excel数据到Gridview public void ReadExcel(string sExcelFile, GridVie...
------分隔线----------------------------汇聚1000万达人的兴趣社区下载即送20张免费照片冲印
扫码下载App
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
运行界面:C#中Invoke类似于C++中的SendMessage(),其会阻塞线程(即同步);而BeginInvoke类似于PostMessage(),其不会阻塞线程(即异步);
阅读(664)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_',
blogTitle:'C#中Invoke与BeginInvoke的区别(另附使用循环创建多个线程)',
blogAbstract:'转自:http://blog.csdn.net/huangwenhua5000/article/details/7921350C#中Invoke与BeginInvoke的区别(另附使用循环创建多个线程)',
blogTag:'',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:0,
publishTime:2,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'',
hmcon:'1',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}502 Bad Gateway
502 Bad Gateway
nginx/1.2.7

我要回帖

更多关于 线程执行顺序 的文章

 

随机推荐