栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

多线程之异步BeginInovke&EndInvoke

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

多线程之异步BeginInovke&EndInvoke

延续上篇 多线程之进程,线程是什么 ,今天说下.NET Framework最开始支持的异步编程方法BeginInovke,EndInvoke。


一、BeginInvoke,EndInvoke方法介绍
  委托.BeginInvoke(委托方法参数,AsyncCallback委托,用户定义的对象);

    BeginInvoke方法启动异步调用。它具有与要异步执行的方法相同的参数,以及两个其他可选参数。第一个参数是AsyncCallback委托,该委托引用异步调用完成时要调用的方法。第二个参数是用户定义的对象,该对象将信息传递到回调方法中。BeginInvoke立即返回,并且不等待异步调用完成。BeginInvoke返回一个IAsyncResult,可用于监视异步调用的进度。
    EndInvoke方法检测异步调用的结果。如果异步调用尚未完成,EndInvoke将阻塞调用线程,直到它完成。其中在异步开始和等待结束间还有其他实用方法,下面会一一介绍。

二、BeginInvoke异步开始,EndInvoke等待Demo

1.不使用EndInvoke异步等待,主线程与异步线程独自完成

static void Main(string[] args)
{
	Console.WriteLine($"********************* 系统开始 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH: mm:ss.fff")} ***************");
	//Action 为框架内置委托
	Action action = Calculate;
	action.BeginInvoke("Test", null, null);
	Console.WriteLine($"********************* 系统结束 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH: mm:ss.fff")} ***************");
	Console.ReadKey();
}

//异步调用方法
static void Calculate(string name)
{
    Console.WriteLine($"*********************异步线程-{name}开始 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
    long num = 0;
    for (int i = 0; i < 10000000; i++)
    {
        num += i;
    }
    //等待2s
    Thread.Sleep(2000);
    Console.WriteLine($"*********************异步线程-{name}结束 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}

    这里的Action为系统框架内置的委托,也可以自己声明委托使用异步,BeginInvoke的两个可选参数暂不使用,输入为null。

2.使用EndInvoke异步等待,EndInvoke方法会等待异步线程完成

static void Main(string[] args)
{
	Console.WriteLine($"********************* 系统开始 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH: mm:ss.fff")} ***************");
	//Action 为框架内置委托
	Action action = Calculate;
	//异步操作返回对象
	IAsyncResult iAsyncResult = action.BeginInvoke("Test", null, null);
	action.EndInvoke(iAsyncResult);
	
	Console.WriteLine($"********************* 系统结束 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH: mm:ss.fff")} ***************");
	Console.ReadKey();
}

//异步调用方法
static void Calculate(string name)
{
    Console.WriteLine($"*********************异步线程-{name}开始 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
    long num = 0;
    for (int i = 0; i < 10000000; i++)
    {
        num += i;
    }
    //等待2s
    Thread.Sleep(2000);
    Console.WriteLine($"*********************异步线程-{name}结束 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}

    与上面的Demo1对比,这里添加了EndInvoke方法,EndInvoke会等待异步执行完成。

三、BeginInvoke,EndInvoke交互中方法细节全面详细介绍

1.BeginInvoke参数细节

static void Main(string[] args)
{
	Console.WriteLine($"********************* 系统开始 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH: mm:ss.fff")} ***************");
	//Action 为框架内置委托
	Action action = Calculate;
	
	//回调委托:异步线程结束后执行
	AsyncCallback callback = call =>
	{
	    Console.WriteLine($"*************** 异步执行状态: {call.IsCompleted} ***************");
	    Console.WriteLine($"*************** 自定义对象:{call.AsyncState.ToString()} ***************");
	    Console.WriteLine($"********************* 回调函数 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH: mm:ss.fff")} ***************");
	};
	
	//自定义对象
	var MyObject = new {
	    Description= "今天我们来使用下BeginInovke&EndInvoke实现异步线程",
	    Date=DateTime.Now,
	    ByPeople="QLWang"
	};
	IAsyncResult iAsyncResult = action.BeginInvoke("Test", callback, MyObject);
	action.EndInvoke(iAsyncResult);
	
	Console.WriteLine($"********************* 系统结束 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH: mm:ss.fff")} ***************");
	Console.ReadKey();
}

//异步调用方法
static void Calculate(string name)
{
    Console.WriteLine($"*********************异步线程-{name}开始 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
    long num = 0;
    for (int i = 0; i < 10000000; i++)
    {
        num += i;
    }
    //等待2s
    Thread.Sleep(2000);
    Console.WriteLine($"*********************异步线程-{name}结束 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}

    这里可以看到,BeginInvoke回调函数也是一个委托,它是在异步完成后执行的,自定义对象可以由我们自定义放入自己需要的信息,回调函数中异步完成状态只有当异步完成为true,异步未完成为false,可以根据这个状态来判断异步是否已完成,既经常说的轮询查看异步的完成状态。

2.IAsyncResult对象中AsyncWaitHandle的用法

static void Main(string[] args)
{
	Console.WriteLine($"********************* 系统开始 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH: mm:ss.fff")} ***************");
	//Action 为框架内置委托
	Action action = Calculate;
	IAsyncResult iAsyncResult = action.BeginInvoke("Test", null, null);
	
	Console.WriteLine($"********************* 异步线程启动 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH: mm:ss.fff")} ***************");
	
	//直接等待异步完成,第一时间进入下一行
	//iAsyncResult.AsyncWaitHandle.WaitOne();
	//一直等待
	//iAsyncResult.AsyncWaitHandle.WaitOne(-1);
	//最多等待1000ms
	iAsyncResult.AsyncWaitHandle.WaitOne(1000);
	//等待
	//action.EndInvoke(iAsyncResult);
	
	Console.WriteLine($"********************* 系统结束 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH: mm:ss.fff")} ***************");
	Console.ReadKey();
}

//异步调用方法
static void Calculate(string name)
{
    Console.WriteLine($"*********************异步线程-{name}开始 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
    long num = 0;
    for (int i = 0; i < 10000000; i++)
    {
        num += i;
    }
    //等待2s
    Thread.Sleep(2000);
    Console.WriteLine($"*********************异步线程-{name}结束 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}

    这里用了WaitOne()等待1000ms后主线程执行,其他方法可以自己试试。

3.多个线程

static void Main(string[] args)
{
	Console.WriteLine($"********************* 系统开始 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH: mm:ss.fff")} ***************");
	//Action 为框架内置委托
	Action action = Calculate;
	
	for (int i = 1; i < 10; i++)
	{
		IAsyncResult iAsyncResult = action.BeginInvoke("Test-" + i, null, null);
	}
	
	Console.WriteLine($"********************* 系统结束 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH: mm:ss.fff")} ***************");
	Console.ReadKey();
}

//异步调用方法
static void Calculate(string name)
{
    Console.WriteLine($"*********************异步线程-{name}开始 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
    long num = 0;
    for (int i = 0; i < 10000000; i++)
    {
        num += i;
    }
    //等待2s
    Thread.Sleep(2000);
    Console.WriteLine($"*********************异步线程-{name}结束 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}

    上面的示例,都是一个异步线程,这里开启了9个异步线程,可以直观的看到多线程执行是快速的,无序的。

四、总结   .NET Framework 允许您异步调用任何方法。定义与您需要调用的方法具有相同签名的委托;公共语言运行库将自动为该委托定义具有适当签名的 BeginInvoke 和 EndInvoke 方法。BeginInvoke 方法用于启动异步调用。它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数。

    BeginInvoke 立即返回,不等待异步调用完成。
    BeginInvoke 返回 IasyncResult,可用于监视调用进度。
    EndInvoke 方法用于检索异步调用结果。调用 BeginInvoke 后可随时调用
    EndInvoke 方法;如果异步调用未完成,EndInvoke 将一直阻塞到异步调用完成。EndInvoke 的参数包括您需要异步执行的方法的 out 和 ref 参数(在 Visual Basic 中为 ByRef 和 ByRef)以及由BeginInvoke 返回的 IAsyncResult。

  四种使用 BeginInvoke 和 EndInvoke 进行异步调用的常用方法。调用了 BeginInvoke 后,可以:

    1.进行某些操作,然后调用 EndInvoke 一直阻塞到调用完成。
    2.使用 IAsyncResult.AsyncWaitHandle 获取 WaitHandle,使用它的 WaitOne 方法将执行一直阻塞到发出 WaitHandle 信号,然后调用EndInvoke。这里主要是主程序等待异步方法,等待异步方法的结果。
    3.轮询由 BeginInvoke 返回的 IAsyncResult,syncResult.IsCompeted确定异步调用何时完成,然后调用 EndInvoke。此处理个人认为与
相同。
    4.将用于回调方法的委托传递给 BeginInvoke。该方法在异步调用完成后在 ThreadPool 线程上执行,它可以调用 EndInvoke。这是在强制装换回调函数里面IAsyncResult.AsyncState(BeginInvoke方法的最后一个参数)成委托,然后用委托执行EndInvoke。

  异步操作的优缺点

    因为异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变量(即使无法完全不用,最起码可以减少共享变量的数量),减少了死锁的可能。当然异步操作也并非完美无暇。编写异步操作的复杂程度较高,程序主要使用回调方式进行处理,与普通人的思维方式有些初入,而且难以调试。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/955334.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号