c# 委托与异步调用

时间:2022-01-16 21:44:47

背景:在winform UI中,有时需要对控件进行比较频繁的刷新,如进度条、picturebox显示视频等。如果在主线程进行这些刷新操作,操作还未完成就将执行下一次刷新,程序将发生错误;如果只是创建另一个线程执行这些操作,将和主线程产生竞争,造成界面锁死(因此windows GUI编程有一个规则,就是只能通过创建控件的线程来操作控件的数据,否则就可能产生不可预料的结果)。这时候,我们就可以用委托与异步来解决这个问题。

  委托:回顾一下委托 ,一、定义委托,委托定义的参数与传递给委托的方法的参数一致。二、声明与实例化。三、调用,将委托作为参数供方法调用。 

  异步:Windows窗体控件,唯一可以从创建它的线程之外的线程中调用的是Invoke()、BeginInvoke()、EndInvoke()方法和InvokeRequired属性。这些方法会切换到创建控件的线程上,以调用赋予一个委托参数的方法,该委托参数可以传递给这些方法。

    关于Invoke()与BeginInvoke():其中BeginInvoke()、EndInvoke()方法是Invoke()方法的异步版本。相同点:都需要一个委托对象作为参数。不同点:Invoke()是同步方法,Invoke封送的方法被执行完毕前,Invoke()不会返回,从而调用者线程将被阻塞;需要等待UI操作执行完毕后继续执行线程时考虑使用。而BeginInvoke()相反,,是异步方法,方法调后立即返回,不用等待委托方法的执行结束,主线程就不会阻塞。

    Delegate.BeginInvoke方法从ThreadPool取出一个线程来执行这个方法,以获得异步执行效果。Control.BeginInvoke方法并不会另开线程。

  参考:

  初学C#,理解的不是很透彻,一步一步来,循序渐进。

  下面是一个进度条的小程序:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace progressBar { public partial class Form1 : Form { delegate void ShowProgressDelegate(); delegate void RunTaskDelegate(); public Form1() { InitializeComponent(); this.progressBar1.Maximum = 50; this.progressBar1.Step = 1; } private void startBtn_Click(object sender, EventArgs e) { RunTaskDelegate runTask = new RunTaskDelegate(RunTask); // 委托异步调用方法 runTask.BeginInvoke(null, null); } private void closeBtn_Click(object sender, EventArgs e) { this.Close(); } //进度条增加 void ShowProgress() { this.progressBar1.PerformStep(); } //模拟工作方法 public void RunTask() { ShowProgressDelegate showProgress = new ShowProgressDelegate(ShowProgress); int iTotal = 50;//工作量 for (int i = 0; i < iTotal; i++) { System.Threading.Thread.Sleep(1000);//模拟工作 this.BeginInvoke(showProgress); } } } }

  我目前的理解是:RunTaskDelegate的作用是从另一个线程调用RunTask()方法(此方法中包含主线程的休眠),可以避免休眠期间界面无响应。ShowProgressDelegate的作用就是异步调用ShowProgress()方法,从而避免非UI线程操作控件产生的异常。