一. BeginInvoke最后两个参数的含义

  倒数第二个参数:指该线程执行完毕后的回调函数;倒数第一个参数:可以向回调函数中传递参数。

下面以一段代码说明:

         /// <summary>
/// 执行动作:耗时而已
/// </summary>
private void TestThread(string threadName)
{
Console.WriteLine("线程开始:线程名为:{2},当前线程的id为:{0},当前时间为:{1},", System.Threading.Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), threadName);
long sum = ;
for (int i = ; i < ; i++)
{
sum += i;
}
Console.WriteLine("线程结束:线程名为:{2},当前线程的id为::{0},当前时间为:{1}", System.Threading.Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), threadName);
}
   private void button1_Click_1(object sender, EventArgs e)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Console.WriteLine("----------------- 二.委托的异步调用测试 --------------------------");
Console.WriteLine("----------------- button1_Click 开始 主线程id为:{0} --------------------------", Thread.CurrentThread.ManagedThreadId); #region 2. 异步回调和异步参数
{
Action<string> myFunc = this.TestThread;
//参数说明:前面几个参数都是方法的参数值,倒数第二个为异步调用的回调函数,倒数第一个为传给回调函数的参数
for (int i = ; i < ; i++)
{
string name = string.Format("button1_Click{0}", i);
myFunc.BeginInvoke(name, t =>
{
Console.WriteLine("我是线程{0}的回调", Thread.CurrentThread.ManagedThreadId);
//用 t.AsyncState 来获取回调传进来的参数
Console.WriteLine("传进来的参数为:{0}", t.AsyncState);
}, "maru");
}
}
#endregion watch.Stop();
Console.WriteLine("----------------- button1_Click 结束 主线程id为:{0} 总耗时:{1}--------------------------", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds);
}

  结果:

二. 异步调用的三种书写

   在上述代码中,我们发现BeginInvoke中,除了我们介绍的最后两个参数外,还有一个参数,传递进去了name,细心的人会发现,正式函数TestThread所需的参数,那么向函数中传递参数到底是在赋值委托时传递,还是委托调用时传递呢?

答案是两者皆可,如果在函数赋值委托时候传递,那么委托调用的时候,BeginInvoke只有最后两个参数。如果在委托调用的时候传递,BeginInvoke除了最后两个参数外,函数本身有几个参数,BeginInvoke前面将多出几个参数位置。

         /// <summary>
/// 两个参数
/// </summary>
public static void TestThread(string txt1, string txt2)
{
Console.WriteLine("线程开始:测试参数为:{0}和{1},当前线程的id为:{2}", txt1, txt2, System.Threading.Thread.CurrentThread.ManagedThreadId);
long sum = ;
for (int i = ; i < ; i++)
{
sum += i;
}
Console.WriteLine("线程结束:测试参数为:{0}和{1},当前线程的id为:{2}", txt1, txt2, System.Threading.Thread.CurrentThread.ManagedThreadId);
}
//1. 方式一(使用多重载Action<>委托,函数的参数在BeginInvoke中传入)
{
Action<string, string> act = TestThread;
IAsyncResult iTest = act.BeginInvoke("参数1", "参数2", t =>
{
Console.WriteLine("我是线程执行后的回调");
Console.WriteLine(t.AsyncState); }, "我是传递参数的位置");
} //2. 方式二(使用Action委托,将参数值直接写在方法中,则无须向BeginInvoke中传入)
{
Action act2 = () => TestThread("参数1", "参数2");
act2.BeginInvoke(t =>
{
Console.WriteLine("我是线程执行后的回调");
Console.WriteLine(t.AsyncState); }, "我是传递参数的位置");
} //3. 方式三(下面两个等价,只不过是第一个省略{},在函数体中将方法写入)
{
Action<string, string> act3 = (a, b) => TestThread(a, b);
//Action<string, string> act3 = (a, b) =>
//{
// TestThread(a, b);
//};
IAsyncResult iTest = act3.BeginInvoke("参数1", "参数2", null, null);
}

三. 线程等待的三种方式

   关于利用委托开启多线程,其线程等待有三种方式:endInvoke、waitone、IsCompleted,推荐使用endInvoke这种方式。

      private void button1_Click_1(object sender, EventArgs e)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Console.WriteLine("----------------- 二.委托的异步调用测试 --------------------------");
Console.WriteLine("----------------- button1_Click 开始 主线程id为:{0} --------------------------", Thread.CurrentThread.ManagedThreadId); #region 4. 异步等待
{
IAsyncResult asyncResult = null;
Action<string> myFunc = this.TestThread;
string name = string.Format("button1_Click{0}", );
asyncResult = myFunc.BeginInvoke(name, t =>
{
Console.WriteLine("我是线程{0}的回调", Thread.CurrentThread.ManagedThreadId);
//用 t.AsyncState 来获取回调传进来的参数
Console.WriteLine("传进来的参数为:{0}", t.AsyncState);
}, "maru"); //等待的方式1:会有时间上的误差
//while (!asyncResult.IsCompleted)
//{
// Console.WriteLine("正在等待中");
//} // 等待的方式二:
//asyncResult.AsyncWaitHandle.WaitOne();//一直等待
//asyncResult.AsyncWaitHandle.WaitOne(-1);//一直等待
//asyncResult.AsyncWaitHandle.WaitOne(1000);//等待1000毫秒,超时就不等待了 //等待的方式三:
myFunc.EndInvoke(asyncResult); }
#endregion watch.Stop();
Console.WriteLine("----------------- button1_Click 结束 主线程id为:{0} 总耗时:{1}--------------------------", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds);
}
#endregion

运行上述代码,我们会发现,主界面又被卡在了,这正好印证了主线程在等待,结果如下:

   

四. 多个线程的等待

上面介绍的是单个线程的等待,有三种方式,那么如果同时开启了多个线程,主线程需要等待这多个线程,这时需要自己写循环,来进行线程等待。

             {
List<IAsyncResult> list = new List<IAsyncResult>();
for (int i = ; i < ; i++)
{
string name1 = string.Format("ypf1-{0}", i);
string name2 = string.Format("ypf2-{0}", i);
Action act = () => TestThread(name1, name2);
IAsyncResult ir = act.BeginInvoke(null, null);
list.Add(ir);
}
//利用委托进行的异步多线程,采用上述方式二的等待最合理的
//缺点:整体上需要写循环,麻烦
foreach (var item in list)
{
item.AsyncWaitHandle.WaitOne();
}
}

.Net进阶系列(11)-异步多线程(委托BeginInvoke)(被替换)的更多相关文章

  1. .Net进阶系列(10)-异步多线程综述(被替换)

    一. 综述 经过两个多个周的整理,异步多线程章节终于整理完成,如下图所示,主要从基本概念.委托的异步调用.Thread多线程.ThreadPool多线程.Task.Parallel并行计算.async ...

  2. .Net进阶系列(15)-异步多线程(线程的特殊处理和深究委托赋值)(被替换)

    1. 线程的异常处理 我们经常会遇到一个场景,开启了多个线程,其中一个线程报错,导致整个程序崩溃.这并不是我们想要的,我需要的结果是,其中一个线程报错,默默的记录下,其它线程正常进行,保证程序整体可以 ...

  3. .Net进阶系列(13)-异步多线程(Task和Parallel)(被替换)

    一. Task开启多线程的三种形式 1. 利用TaskFactory下的StartNew方法,向StartNew传递无参数的委托,或者是Action<object>委托. 2. 利用Tas ...

  4. .Net进阶系列(12)-异步多线程(Thread和ThreadPool)(被替换)

    一. Thread多线程   1. 两种使用方式 通过F12查看Thread后,发现有两类构造函数,ParameterizedThreadStart和ThreadStart,其中 ThreadStar ...

  5. .Net进阶系列(14)-异步多线程(async和await)(被替换)

    1.  方法名前只有async,但是方法中Task实例前没有await关键字,该方法和普通方法没有什么区别,但是会报一个警告. #region 情况一 /// <summary> /// ...

  6. SilkTest高级进阶系列9 – 异步执行命令

    我们常常会使用sys_execute函数执行一些外部的程序或者命令来做一些事情,但是由于sys_execute是一个同步的函数,它会等待执行的命令完成后才会返回.在大多数情况下,这个函数足够用了. 但 ...

  7. .NET进阶篇-语言章-2-Delegate委托、Event事件

    知识只有经过整理才能形成技能 整个章节分布简介请查看第一篇 内容目录 一.概述 二.解析委托知识点 1.委托本质 2.委托的使用 3.委托意义 逻辑解耦,减少重复代码 代码封装支持扩展 匿名方法和La ...

  8. 异步和多线程,委托异步调用,Thread,ThreadPool,Task,Parallel,CancellationTokenSource

    1 进程-线程-多线程,同步和异步2 异步使用和回调3 异步参数4 异步等待5 异步返回值 5 多线程的特点:不卡主线程.速度快.无序性7 thread:线程等待,回调,前台线程/后台线程, 8 th ...

  9. .NET进阶篇06-async异步、thread多线程1

    知识需要不断积累.总结和沉淀,思考和写作是成长的催化剂 异步多线程挺大一块内容,既想拆开慢慢学,又想一股脑全倒出.纠结再三,还是拆开吃透,也不至于篇幅过长,劝退许多人 本篇先做一个概述,列明一些基本概 ...

随机推荐

  1. 如何将数据库引擎配置为侦听多个 TCP 端口

    SQL Server 2005         为 SQL Server 启用 TCP/IP 后,数据库引擎将侦听连接点上是否有传入的连接(由 IP 地址和 TCP 端口号组成).下列步骤将创建一个表 ...

  2. js选择排序。

    <script> , , , , , , , ]; ; j<len; j++ ){ // 假设min为最小值 var minIndex = j; var min = arr[j]; ...

  3. mongo扩展错误

    make: *** [php_mongo.lo] Error 1 Ask Question 0   When I installed the Mongo PHP extension, the foll ...

  4. 工资薪金VS劳务报酬

    工资薪金所得与劳务报酬所得两个征税项目在个人所得税应纳税所得额的计算.征收标准等方面都有所不同,因而在实际操作中不可相互混淆. 工资薪金所得属于非独立个人劳务活动,即在机关.团体.学校.部队.企事业单 ...

  5. 通过几个例子看sed的模式空间与保持空间

    SED之所以能以行为单位的编辑或修改文本,其原因在于它使用了两个空间:一个是活动的“模式空间(pattern space)”,另一个是起辅助作用的“暂存缓冲区(holdingspace)这2个空间的使 ...

  6. HDU4287-STL模拟水题

    一场2012天津网络预选赛的题,签到题. 但是还是写了三四十分钟,C++和STL太不熟悉了,总是编译错误不知道怎么解决. 一开始用的Char [] 后来改成了string,STL和string搭配起来 ...

  7. 一个死循环导致的栈溢出实例:StackOverFlowError

    有一个功能,要用复选框组做成单选框效果,如果有三个复选框 CheckBox ,并且保证每次只能选中一个.刚开始添加了以下的值改变后的监听方法 addValueChangeListener ,却导致了栈 ...

  8. MT【217】韦达定理应用

    若2018次方程$x^{2018}-4036x^{2017}+a_{2016}x^{2016}+\cdots+a_1x+a_0=0$ 有2018个正实数, 则对于所有可能的方程$\sum\limits ...

  9. 自学Linux Shell7.3-linux共享文件

    点击返回 自学Linux命令行与Shell脚本之路 7.3-linux共享文件 在linux系统中共享文件的方法是通过创建组. 1. linux为每个文件和目录存储了3个额外的信息位: SUID设置用 ...

  10. Android 视频 教程 源码 电子书 网址

    资源名称 资源地址 下载量 好评率8天快速掌握Android视频教程67集(附源码)http://down.51cto.com/zt/2197 32157Android开发入门之实战技巧和源码 htt ...