简单地使用线程之一:使用异步编程模型

.NetFramework的异步编程模型从本质上来说是使用线程池来完成异步的任务,异步委托、HttpWebRequest等都使用了异步模型。

这里我们使用异步委托来说明异步编程模型。

首先,我们来明确一下,对于多线程来说,我们需要关注哪些问题。

“线程是一段执行中的代码流”,从这句话中,我们可以关注这段代码流何时开始执行、何时结束、从主线程如何传递参数至从子线程、从子线程如何返回结果至主线程?

问题1:在异步编程模型中,子线程何时开始执行?

对于异步编程模型来说,使用BeginXXX来开始执行线程。

我们首先来看一个实例(来自《C#高级编程(第7版)》)。

pulic delegate int TestMethodDelegate(int data,int ms);

static int TestMethod(int data,int ms)

{

    Console.WriteLine("TestMethod started.");

    Thread.Sleep(ms);

    Console.WriteLine("TestMethod completed.");

    return data++;

}

 

static Main()

{

    TestMethodDelegate dl=TestMethod;

    IAsyncResult ar=dl.BeginInvoke(1,3000,null,null);

    //其中,1,3000分别对应委托的两个参数,后面两个null位置的参数是两个固定的参数,是留给异步回调的时候用的(稍后会讲到)。

}

问题2:在异步编程模型中,子线程何时结束?

其实,这个问题对于只有一个主线程的单线程程序来说不会成为问题,因为一段代码执行过程中,执行到这个方法的最后一行,这个方法也就执行结束了。

但在多线程中,由于主线程与子线程有一个从属关系,加上线程还有前台线程与后台线程之分,使得子线程的结束时机需要加以判断,判断规则如下。

(1).如果子线程是后台线程,那么主线程结束时,不管子线程是否执行完毕,子线程将结束。

(2).如果子线程是前强线程,那么主线程结束时,子线程将继续执行,直至结束。

PS:由于线程池里面的线程均为后台线程,因此,异步编程模型中,只会存在第1种情况。

那么问题来了:如果子线程为后台线程,且子线程的处理时间非常地长,那主线程如果处理到最后一条代码时,那子线程就会关闭,那子线程的处理逻辑就没走完,就会出错。

我们该如何防止这样的问题发生呢?

直观地来说,在主线程中等待子线程完成是一种可行的方案,而要实现这种方案,我们至少需要知道子线程是否已经执行完成?

在异步编程模型中,我们可以使用轮询(Polling)、等待句柄(WaitHandler)、EndInvoke()方法来达到这样的效果。

方法1:轮询(Polling)

我们改写Main()方法如下:

static Main()

{

    TestMethodDelegate dl=TestMethod;

    IAsyncResult ar=dl.BeginInvoke(1,3000,null,null);

    while(!ar.IsCompleted)

    {

         //do something 

   }

   //执行到这里的时候,子线程已经执行完毕了。

}

方法2:等待句柄(WaitHandler)

我们再次改写Main()方法。

static Main()

{

    TestMethodDelegate dl=TestMethod;

    IAsyncResult ar=dl.BeginInvoke(1,3000,null,null);

    while(true)

    {

         if(ar.AsyncWaitHandler.WaitOne(50,false))//每等待50毫秒,然后判断是否子线程是否已完成,如果已完成,则break。

        {

              //执行到这里的时候,子线程已经执行完毕了。

            break;

        }

    }

   //主线程....

}

方法3:EndInvoke()方法

再次改用Main()方法:

static Main()

{

    TestMethodDelegate dl=TestMethod;

    IAsyncResult ar=dl.BeginInvoke(1,3000,null,null);

    ar.EndInvoke();//会一直等待,直到委托方法执行完成,其实,也可以通过它将子线程的结果返回至主线程中,可以参考后面的问题4.

   //主线程....

}

问题3:在异步编程模型中,从主线程如何传递参数至从子线程?

这个问题我们其实已经在问题1中有所展示,我们在BeginInvoke的方法中向委托中传递了两个参数。

 

问题4:在异步编程模型中,从子线程如何返回结果至主线程?

在问题2中,我们使用EndInvoke()等待委托方法执行完成,其实我们也可采用这个方法得到子线程的返回值。

static Main()

{

    TestMethodDelegate dl=TestMethod;

    IAsyncResult ar=dl.BeginInvoke(1,3000,null,null);

    int result=ar.EndInvoke();//得到返回值,返回值类型这里为int,即委托定义的返回值类型。    

   //主线程....

}

 

问题5:异步回调

异步回调很有用,它的出现应该说是极大地扩展了异步模型下的多线程威力。为什么这么说?

考虑一种情况,如果主线程需要120秒完成执行,子线程需要80秒,在主线程第90秒的时候开始有启动子线程,主线程等待子线程结束,最后结束主线程。那么,整个主线程就至少需要90+80秒的时间来完成执行,中间很明显有不少主线程等待子线程的时间。有没有更好的办法?

如果主线程不需要子线程的返回结果,而子线程结束后还会有一些单独的处理逻辑(比如清理对象、单独的处理数据等),我们可以采用异步回调来解决这个问题。

异步回调:简而言之,就是异步任务完成之后,再回过头来调用的方法。

先看一个异步回调的例子:

static Main()

{

    TestMethodDelegate dl=TestMethod;

    IAsyncResult ar=dl.BeginInvoke(1,3000,TestMethodCompleted,dl);

   //主线程....

}

static TestMethodCompleted(IAsyncResult ar)

{

     //do something

    TestMethodDelegate dl=(TestMethodDelegate )ar.AysncState;

    dl.EndInvoke();//可以传递委托实例进来,在这里获得委托线程的结果。    

}

 

在这里,我们关注BeginInvoke方法的第3个和第4个参数。

第3个参数,TestMethodCompleted即是回调方法,它是一个AsyncCallback类型的委托。

第4个参数是传递给回调方法的参数,在回调方法中可以用ar.AsyncState来获得这个参数。

使用回调方法,必须注意这个方法要从委托线程中调用,而不是从主线程中调用,如果从主线程中调用,那就变成了普通的方法调用。

另外,如果使用Lambda表达式可以实现一些更简洁优雅的偌。 

static Main() 

    TestMethodDelegate dl=TestMethod; 

    dl.BeginInvoke(1,3000,

        ar=>{

                     int result=dl.EndInvoke(ar);

                     //do something

                },

        null) ;

   //主线程.... 

//这里,不需要把一个值赋予BeginInvoke()方法的最后一个参数,因为Lambda表达式可以直接访问该作用域外部的变量dl。但是,Lambsa表达式的实现代码仍是从委托线程中调用,只是不是很明显。

 

转载于:https://www.cnblogs.com/gudi/p/4499155.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/257311.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ShowType=0,交换机命令showinterfacestype0/port_#switchport|trunk用于显 - 信管网

交换机命令show interfaces type0/port_# switchport|trunk用于显示中继连接的配置情况,下面是显示例子:2950#show interface fastEthernet0/1 switchportName: fa0/1Switchport: EnabledAdministrative mode: trunkOperational Mode: trunkAdministrati…

SQL SERVER学习笔记(二)数据库管理

第二部分:数据库管理 单词记忆:transact:处理 create:创建 execute:执行、完成 一、 SQL Server的特性 1、 安装简便:为了便于安装、使用和管理,SQL Server2000提供了一组管理和开发工具。 …

SQL——快速定位相关的外键表

转载于:https://www.cnblogs.com/mingle/p/4506422.html

Linux安装glibc(升级版本)

2019独角兽企业重金招聘Python工程师标准>>> glibc下载地址:http://ftp.gnu.org/gnu/glibc/ 这里下载 glibc-2.15: http://ftp.gnu.org/gnu/glibc/glibc-2.15.tar.gz glibc-ports-2.15: http://ftp.gnu.org/gnu/glibc/glibc-ports…

定义列表的特点html,HTML的列表表格表单知识点

无序列表格式 有序列表格式第一项 …

Javascript 获取url参数,hash值 ,cookie

/*** 获取请求参数* param key* returns {*}*/ function getRequestParameter(key){var params getRequestParameters();return params[key]; }/*** 获取请求参数列表* returns {{}}*/ function getRequestParameters(){var arr (location.search || "").replace(/…

C# 中使用 ThoughtWorks.QRCode.dll 生成指定尺寸和边框宽度的二维码

本文介绍在 C# 中使用 ThoughtWorks.QRCode.dll 生成指定尺寸和边框宽度的二维码。网上文章大多只是简单介绍内置参数的设置,根据我的使用目的,增加了自定义目标二维码图片尺寸和白边边框。有需要的朋友们可以试一下,如有bug欢迎指正。 首先&…

html设置百度协议,网站HTML结构SEO要求说明(含移动站)

网页结构一、网页中主体结构标签一一对应。网页头部区域网页底部区域网页边框区域网页导航区域网页章节、页眉、页脚详情页文章区域详情页作者信息详情页中文章的发布日期列表页中文章列表区域二、其他说明1、首页head中标注Meta标签协议,标识对应的网页浏览&#x…

【Fanvas技术解密】HTML5 canvas实现脏区重绘

先说明一下,fanvas是笔者在企鹅公司开发的,即将开源的flash转canvas工具。 脏区重绘(dirty rectangle)并不是一门新鲜的技术了,这在最早2D游戏诞生的时候就已经存在。 复杂的术语或概念就不多说,简单说,脏区重绘就是每…

javascript 学习教程

1&#xff0c;JavaScript 是一种轻量级的编程语言&#xff0c;是可插入 HTML 页面的编程代码。 2,以<script>标签开始&#xff0c;以</script>标签结束。 3,引用放在外部文件的扩展名为.js的脚本文件 <script src"myScript.js"></script> 4&…

我国非按劳分配收入

我国现阶段非按劳分配收入探讨 我国社会主义初级阶段的个人消费品分配&#xff0c;是通过多种分配方式实现的。除了占主体地位的按劳分配方式之外&#xff0c;还存在许多种类的非按劳分配方式。因此&#xff0c;在我国城乡居民个人收入总额中&#xff0c;除了一部分按劳分配收入…

html调用js里面的函数,html如何调用js函数

html调用js函数的方法&#xff1a;1、用控件本身进行调用&#xff1b;2、通过javascript中的时间控件定时执行&#xff1b;3、通过getElementById调用&#xff1b;4、通过document.getElementsByName调用。本文操作环境&#xff1a;Windows7系统、html5&&javascript1.8…

大部分人都会做错的经典JS闭包面试题

2019独角兽企业重金招聘Python工程师标准>>> 由工作中演变而来的面试题 JS中有几种函数 创建函数的几种方式 三个fun函数的关系是什么&#xff1f; 函数作用域链的问题 到底在调用哪个函数&#xff1f; 后话 转载于:https://my.oschina.net/u/3687565/blog/1549046

STM32片上Flash内存映射、页面大小、寄存器映射

转自&#xff1a;http://blog.chinaunix.net/uid-20617446-id-3847242.html 一、怎么看Flash大小 1.1 通过型号 型号会印在MCU表面&#xff0c;可以通过观察获得&#xff0c;我的是STM32F103RBT6(以下分析基于这个型号)&#xff0c;对照下图的STM32产品命名&#xff0c;可知STM…

如何设计实现一个地址反解析服务?

http://www.cnblogs.com/LBSer/p/4507829.html 一、什么是地址反解析 我们都知道手机定位服务&#xff0c;其本质是汇总各种信号得出一个经纬度坐标&#xff08;x,y&#xff09;&#xff08;具体定位原理可以参考&#xff1a;LBS定位技术、基于朴素贝叶斯的定位算法&#xff09…

html5 hr代码缩减比例,HTML HR size用法及代码示例

DOM HR size属性用于设置或返回元素的size属性的vlue。用法:它返回HR大小属性。hrobject.size用于设置HR大小属性。hrobject.size"value"属性值&#xff1a;value:它包含指定HR元素高度的像素值。返回值&#xff1a;它返回一个字符串值&#xff0c;该值代表HR元素的高…

【转】C++标准转换运算符static_cast

static_cast<new_type> (expression) 虽然const_cast是用来去除变量的const限定&#xff0c;但是static_cast却不是用来去除变量的static引用。其实这是很容易理解的&#xff0c;static决定的是一个变量的作用域和生命周期&#xff0c;比如&#xff1a;在一个文件中将变量…

MySQL Binlog Mixed模式记录成Row格式

背景&#xff1a; 一个简单的主从结构&#xff0c;主的binlog format是Mixed模式&#xff0c;在执行一条简单的导入语句时&#xff0c;通过mysqlbinlog导出发现记录的Binlog全部变成了Row的格式&#xff08;明明设置的是Mixed&#xff09;&#xff0c;现在就说说在什么情况下Bi…

update语句中使用子查询

55. 更改 108 员工的信息: 使其工资变为所在部门中的最高工资, job 变为公司中平均工资最低的 job 1). 搭建骨架 update employees set salary (), job_id () where employee_id 108; 2). 所在部门中的最高工资 select max(salary) from employees where department_id ( s…

html后台数据分类管理,细分数据.html

&#xfeff;细分数据$axure.utils.getTransparentGifPath function() { return resources/images/transparent.gif; };$axure.utils.getOtherPath function() { return resources/Other.html; };$axure.utils.getReloadPath function() { return resources/reload.html; };…