线程间通信-事件

事件(Event)是一种使对象或类能够提供通知的成员,可以用来实现在线程间通信。这在GUI程序中很常见。为了UI的流畅,通常会把耗时的任务放到单独的线程上执行,等任务执行结束后把结果更新到UI上。

案例说明

点击按钮,开始下载(模拟耗时工作),下载进度更新到进度条上。 

bf4ece4d180487ecbbb069e555c02190.gif

代码结构

7a6121556d49edbd94f95c509316002f.png

声明事件 DownloadFileProgressEvent

/// <summary>
/// 业务类
/// </summary>
public class BusinessService : IBusinessService
{
    /// <summary>
    /// 声明event
    /// </summary>
    public event EventHandler DownloadFileProgressEvent;

    /// <summary>
    /// 模拟耗时任务接口
    /// </summary>
    public void DownloadFile()
    {
        int totleTime = 0;

        // 模拟5s下载完成
        while (totleTime < 5)
        {
            totleTime += 1;
            // 模拟耗时任务
            Thread.Sleep(1000);
            // 触发进度更新
            DownloadFileProgressEvent.Invoke(this, new DownloadProgressEventArgs(totleTime * 20));
        }
    }
}

订阅事件 DownloadFileProgressEvent

public MainWindowViewModel(IBusinessService businessService)
{
    _service = businessService;
    // 订阅事件
    _service.DownloadFileProgressEvent += _service_DownloadFileProgressEvent;

    // 按钮点击命令
    StartBtnCommand = new DelegateCommand(ExecuteStart);
}
/// <summary>
/// 按钮点击事件
/// </summary>
private void ExecuteStart()
{
    Console.WriteLine(string.Format("begin task : thread = {0}", Thread.CurrentThread.ManagedThreadId));

    // 开启新的线程执行耗时任务
    Task.Run(() => {
        _service.DownloadFile();
    }).ContinueWith(t => {
        Console.WriteLine(string.Format("task continue : thread = {0}", Thread.CurrentThread.ManagedThreadId));
    });

    Console.WriteLine(string.Format("end task : thread = {0}", Thread.CurrentThread.ManagedThreadId));
}
/// <summary>
/// event 回调
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void _service_DownloadFileProgressEvent(object sender, System.EventArgs e)
{
    ProgressBarValue = ((DownloadProgressEventArgs)e).ProgressValue;
    Console.WriteLine(string.Format("event-callback : sender = {0}, eventArgs = {1}, thread = {2}", sender, e, Thread.CurrentThread.ManagedThreadId));
}

日志打印

a26c777c60c8d819d92b50feccddd21f.png

  • • 按钮执行方法 ExecuteStart 很快就执行结束,不会阻塞UI线程

  • • 耗时任务 Download 在单独的线程上执行

  • • Task.ContinueWith 默认会在新的线程上执行(可以改变)

委托链

如果事件订阅两次,会发生什么呢?

public MainWindowViewModel(IBusinessService businessService)
{
    _service = businessService;
    // 订阅事件
    _service.DownloadFileProgressEvent += _service_DownloadFileProgressEvent;
    // 再次订阅事件
    _service.DownloadFileProgressEvent += _service_DownloadFileProgressEvent;

    // 按钮点击命令
    StartBtnCommand = new DelegateCommand(ExecuteStart);
}

从日志可以看出,event 回调方法会执行两次。

8e1e9c08e02faedca7bdf3cc78687bf0.png

当事件的使用者订阅该事件时,其本质就是将事件的处理方法加入到委托链中。订阅事件的本质就是调用 Delegate 的 Combine 方法将事件处理方法加入到委托链中。

-

技术群:添加小编微信并备注进群

小编微信:mm1552923   

公众号:dotNet编程大全    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值