.NET异步编程模式(三)

EAP(Event-based Asynchronous Pattern) 是基于事件的异步模式,在 .NET Framework 2.0 中引入。EAP 需要一个有 Async 后缀方法和一个或多个事件。EAP不再推荐用于新开发

EAP

一个符合 EAP 模式的示例声明如下:

public class AsyncExample  
{  
    // Synchronous methods.  
    public int Method1(string param);  
    public void Method2(double param);  
  
    // Asynchronous methods.  
    public void Method1Async(string param);  
    public void Method1Async(string param, object userState);  
    public event Method1CompletedEventHandler Method1Completed;  
  
    public void Method2Async(double param);  
    public void Method2Async(double param, object userState);  
    public event Method2CompletedEventHandler Method2Completed;  
  
    public void CancelAsync(object userState);  
  
    public bool IsBusy { get; }  
  
    // Class implementation not shown.  
}

BackgroundWorker

SoundPlayer和PictureBox组件表示基于事件的异步模式的简单实现。WebClient和BackgroundWorker组件代表了基于事件的异步模式的更复杂的实现。

 private void EAP_Btn_Click(object sender, RoutedEventArgs e)
{
    // 记录时间
    Debug.WriteLine("1-" + DateTime.Now.TimeOfDay.ToString() +
                    ",ThreadID = " + Thread.CurrentThread.ManagedThreadId);

    BackgroundWorker worker = new BackgroundWorker();
    // 事件绑定
    worker.DoWork += Worker_DoWork;
    worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
    // 异步执行
    worker.RunWorkerAsync();

    // 记录时间
    Debug.WriteLine("4-" + DateTime.Now.TimeOfDay.ToString() +
                    ",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
}

private void Worker_RunWorkerCompleted(object? sender, RunWorkerCompletedEventArgs e)
{
    // 记录时间
    Debug.WriteLine("3-" + DateTime.Now.TimeOfDay.ToString() +
                    ",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
}

private void Worker_DoWork(object? sender, DoWorkEventArgs e)
{
    // 访问外网网站网站
    var req = WebRequest.Create("https://docs.newrelic.com/docs/apm/agents/net-agent/getting-started/net-agent-compatibility-requirements-net-framework/");
    req.GetResponse();

    Debug.WriteLine("2-" + DateTime.Now.TimeOfDay.ToString() +
                    ",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
}

程序运行效果:

在这里插入图片描述

日志输出:

1-17:00:10.3584040,ThreadID = 1
4-17:00:10.3609798,ThreadID = 1
2-17:00:11.3485887,ThreadID = 8
3-17:00:11.3514632,ThreadID = 1

从效果和日志上看:

  • EAP 不会阻塞调用线程
  • 异步操作真正执行是在另外一个线程
  • RunWorkerCompleted 回调会回到调用线程(UI线程)

和APM比起来很像,好像只是把 委托绑定 放到了外面。
我们可以看一下 BackgroundWorker 的源码:

  • 在构造函数里实例化一个委托 threadStart

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 调用 RunWorkerAsync() 方法

在这里插入图片描述

看起来 EAP 的本质,还是使用了委托的异步方式(BeginInvoke),实质上还是 APM 异步模式。

多任务

如果有多个异步任务,我们希望按照先后顺序执行,并且需要在调用线程上得到所有返回值。

public Func<string, string> func1()
{
    return new Func<string, string>(t =>
    {
        Thread.Sleep(2000);
        return "name:" + t;
    });
}
public Func<string, string> func2()
{
    return new Func<string, string>(t =>
    {
        Thread.Sleep(2000);
        return "age:" + t;
    });
}
public Func<string, string> func3()
{
    return new Func<string, string>(t =>
    {
        Thread.Sleep(2000);
        return "sex:" + t;
    });
}

// 按照一定的顺序去执行
public void Multi_APM_Btn_Click(object sender, RoutedEventArgs e)
{
    string str1 = string.Empty, str2 = string.Empty, str3 = string.Empty;
    IAsyncResult asyncResult1 = null, asyncResult2 = null, asyncResult3 = null;

    asyncResult1 = func1().BeginInvoke("张三", t =>
    {
        str1 = func1().EndInvoke(t);
        Debug.WriteLine("1-" + DateTime.Now.TimeOfDay.ToString() +",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
        asyncResult2 = func2().BeginInvoke("26", a =>
        {
            str2 = func2().EndInvoke(a);
            Debug.WriteLine("2-" + DateTime.Now.TimeOfDay.ToString() +",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
            asyncResult3 = func3().BeginInvoke("男", s =>
            {
                str3 = func3().EndInvoke(s);
                Debug.WriteLine("3-" + DateTime.Now.TimeOfDay.ToString() + ",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
            }, null);
        }, null);
    }, null);

    asyncResult1.AsyncWaitHandle.WaitOne();
    asyncResult2.AsyncWaitHandle.WaitOne();
    asyncResult3.AsyncWaitHandle.WaitOne();
    Debug.WriteLine(str1 + str2 + str3);
} 

运行起来,发现有异常:

由此可见在完成第一个异步操作之前没有对asyncResult2进行赋值,asyncResult2执行异步等待的时候报异常。也可以有其他方法来解决这个问题,但会比较复杂。

微信公众号 - 希夏普
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值