C#多线程(5)

一、回调

先来看看异步多线程无序的例子:

在界面上新增一个按钮,实现代码如下:

private void btnAsyncAdvanced_Click(object sender, EventArgs e)
{
      Console.WriteLine($"****************btnAsyncAdvanced_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
      Action<string> action = this.DoSomethingLong;
      action.BeginInvoke("btnAsyncAdvanced_Click", null, null);
      // 需求:异步多线程执行完之后再打印出下面这句
      Console.WriteLine($"到这里计算已经完成了。{Thread.CurrentThread.ManagedThreadId.ToString("00")}。");
      Console.WriteLine($"****************btnAsyncAdvanced_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}

运行结果如下所示:

 

从上面的截图中看出,最终的效果并不是我们想要的效果,而且打印输出的还是主线程。

既然异步多线程是无序的,那我们有没有什么办法可以解决无序的问题呢?办法当然是有的,那就是使用回调,.NET框架已经帮我们实现了回调:

BeginInvoke的第二个参数就是一个回调,那么AsyncCallback究竟是什么呢?F12查看AsyncCallback的定义:

 

发现AsyncCallback就是一个委托,参数类型是IAsyncResult,明白了AsyncCallback是什么以后,将上面的代码进行如下的改造:

private void btnAsyncAdvanced_Click(object sender, EventArgs e)
{       
    Console.WriteLine($"****************btnAsyncAdvanced_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
    Action<string> action = this.DoSomethingLong;
    // 定义一个回调
    AsyncCallback callback = p => 
    {
       Console.WriteLine($"到这里计算已经完成了。{Thread.CurrentThread.ManagedThreadId.ToString("00")}。");
    };
    // 回调作为参数
    action.BeginInvoke("btnAsyncAdvanced_Click", callback, null);          
    Console.WriteLine($"****************btnAsyncAdvanced_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
 }

上面的截图中可以看出,这就是我们想要的效果,而且打印是子线程输出的,但是程序究竟是怎么实现的呢?我们可以进行如下的猜想:

程序执行到BeginInvoke的时候,会申请一个基于线程池的线程,这个线程会完成委托的执行(在这里就是执行DoSomethingLong()方法),在委托执行完以后,这个线程又会去执行callback回调的委托,执行callback委托需要一个IAsyncResult类型的参数,这个IAsyncResult类型的参数是如何来的呢?鼠标右键放到BeginInvoke上面,查看返回值:

发现BeginInvoke的返回值就是IAsyncResult类型的。那么这个返回值是不是就是callback委托的参数呢?将代码进行如下的修改:

private void btnAsyncAdvanced_Click(object sender, EventArgs e)
{
            // 需求:异步多线程执行完之后再打印出下面这句
            Console.WriteLine($"****************btnAsyncAdvanced_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            Action<string> action = this.DoSomethingLong;
            // 无序的
            //action.BeginInvoke("btnAsyncAdvanced_Click", null, null);

            IAsyncResult asyncResult = null;
            // 定义一个回调
            AsyncCallback callback = p =>
            {
                // 比较两个变量是否是同一个
                Console.WriteLine(object.ReferenceEquals(p,asyncResult));
                Console.WriteLine($"到这里计算已经完成了。{Thread.CurrentThread.ManagedThreadId.ToString("00")}。");
            };
            // 回调作为参数
            asyncResult= action.BeginInvoke("btnAsyncAdvanced_Click", callback, null);           
            Console.WriteLine($"****************btnAsyncAdvanced_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}

这里可以看出BeginInvoke的返回值就是callback委托的参数。

现在我们可以使用回调解决异步多线程无序的问题了。

二、获取委托异步调用的返回值

使用EndInvoke可以获取委托异步调用的返回值,请看下面的例子:

private void btnAsyncReturnVlaue_Click(object sender, EventArgs e)
{
       // 定义一个无参数、int类型返回值的委托
       Func<int> func = () =>
       {
             Thread.Sleep(2000);
             return DateTime.Now.Day;
       };
       // 输出委托同步调用的返回值
       Console.WriteLine($"func.Invoke()={func.Invoke()}");
       // 委托的异步调用
       IAsyncResult asyncResult = func.BeginInvoke(p => 
       {
            Console.WriteLine(p.AsyncState);
       },"异步调用返回值");
       // 输出委托异步调用的返回值
       Console.WriteLine($"func.EndInvoke(asyncResult)={func.EndInvoke(asyncResult)}");
}

运行结果如下所示: 

 

注意:本内容来自https://www.cnblogs.com/dotnet261010/p/6159984.html

猜你喜欢

转载自blog.csdn.net/xiaochenXIHUA/article/details/89381658