(一)使用规则
public IAsyncResult BeginInvoke(Delegate method);
public IAsyncResult BeginInvoke(Delegate method, params object[] args);
①:传递一个委托
this.BeginInvoke(new InvokeMethod(InvokeXXX), new object[] { listDgv });
public delegate void InvokeMethod(object x);
public void InvokeXXX(object x)
{
dgvMain.DataSource = x;
}
②:传递系统委托 public delegate void Action<T>(T obj)
this.BeginInvoke(new Action
③:传递匿名委托
算法定义
IAsyncResult asyncResult = this.BeginInvoke(new Action<object>(t => {
//object temp = t;
dgvMain.DataSource = t;
int x = 0;
MessageBox.Show("导入成功");
}), new object[] { listDgv });
④:传递无参委托
this.BeginInvoke(new Action(()=>{
progressBar1.Value = 0;
}));
(二)BeginInvoke,EndInvoke在异步线程中的应用
2.1 线程概述
在操作系统中一个进程至少要包含一个线程,然后,在某些时候需要在同一个进程中同时执行多项任务,或是为了提供程序的性能,将要执行的任务分解成多个子任务执行。这就需要在同一个进程中开启多个线程。我们使用C#编写一个应用程序(控制台或桌面程序都可以),然后运行这个程序,并打开windows任务管理器,这时我们就会看到这个应用程序中所含有的线程数,如下图所示。
**:如果任务管理器没有“线程数”列,可以【查看】>【选择列】来显示“线程计数”列。从上图可以看出,几乎所有的进程都拥有两个以上的线程。从而可以看出,线程是提供应用程序性能的重要手段之一,尤其在多核CPU的机器上尤为明显。
2.2 用委托(Delegate)的BeginInvoke和EndInvoke方法操作线程
在C#中使用线程的方法很多,使用委托的BeginInvoke和EndInvoke方法就是其中之一。BeginInvoke方法可以使用线程异步地执行委托所指向的方法。然后通过EndInvoke方法获得方法的返回值(EndInvoke方法的返回值就是被调用方法的返回值),或是确定方法已经被成功调用。我们可以通过四种方法从EndInvoke方法来获得返回值。
代码分析
using System;
using System.Threading;
namespace MyAsyncThread
{
class Program
{
private static int newTask(int ms)
{
Console.WriteLine("任务开始");
Thread.Sleep(ms);
Random random = new Random();
int n = random.Next(10000);
Console.WriteLine("任务完成");
return n;
}
private delegate int NewTaskDelegate(int ms);
static void Main(string[] args)
{
NewTaskDelegate task = newTask;
IAsyncResult asyncResult = task.BeginInvoke(5000, null, null);
// EndInvoke方法将被阻塞5秒
int result = task.EndInvoke(asyncResult);
Console.WriteLine(result);
Console.ReadKey();
}
}
}
关于BeginInvoke函数的分析:
**:在❶处有一个AsyncCallback的委托,该委托的定义为:public delegate void AsyncCallback(IAsyncResult ar),因此异步回调函数要以这个委托来进行签名调用。
BeginInvoke方法可启动异步调用。它与您需要异步执行的方法具有相同的参数,另外它还有两个可选参数。第一个参数是一个AsyncCallback 委托,该委托引用在异步调用完成时要调用的方法。第二个参数是一个用户定义的对象,该对象可向回调方法传递信息。BeginInvoke立即返回,不等待异步调用完成。BeginInvoke会返回IAsyncResult,这个结果可用于监视异步调用进度。
关于EndInvoke函数的分析:
EndInvoke方法检索异步调用的结果。调用BeginInvoke后可随时调用 EndInvoke 方法;如果异步调用尚未完成,EndInvoke 将一直阻止调用线程,直到异步调用完成后才允许调用线程执行。EndInvoke的参数包括您需要异步执行的方法的out和ref参数以及由BeginInvoke返回的IAsyncResult。
当我们Main()中加入Thread.Sleep(15000);等待15钟,再调用BeginInvoke(),任务管理器中线程数的变化如下图:说明在调用BeginInvoke()后系统会创建一个线程。
2.3 BeginInvoke和EndInvoke非阻塞式操作线程
如下图所示有这样一个UI,在执行某个耗时操作时,同时要在界面显示耗时操作运行的时间以告知终端用户。
代码实现namespace MyAsyncThreadWin
{
public partial class Form1 : Form
{
int time = 1;
public Form1()
{
InitializeComponent();
}
private string LongTimeTask()
{
Thread.Sleep(10000);
return "执行完成";
}
private void Callback(IAsyncResult ar)
{
string str=(ar.AsyncState as Func<string>).EndInvoke(ar);
this.timer1.Enabled = false;
time = 1;
this.label1.Text = str;
}
private void btnStart_Click(object sender, EventArgs e)
{
Func<string> func = LongTimeTask;
timer1.Enabled = true;
IAsyncResult result = func.BeginInvoke(Callback,func);
}
private void timer1_Tick(object sender, EventArgs e)
{
label1.Text = "执行时间:" + time.ToString();
time++;
}
}
}
代码分析:
(三)异步调用的异常处理过程。如下图产生的异常错误信息提示到终端用户。
代码实现namespace MyAsyncThreadWin
{
public partial class Form1 : Form
{
int time = 1;
public Form1()
{
InitializeComponent();
}
private string LongTimeTask()
{
try
{
DataTable dt = null;
dt.WriteXml("xxx.xml");
Thread.Sleep(10000);
return "执行完成";
}
catch (Exception)
{
Action<string> action = ExAction; //定义一个委托来处理异常。
this.timer1.Enabled = false;
action("Error"); //调用委托方法来处理异常信息。
return "Error";
//throw new Exception("Error"); 此处为线程堆栈中的异常,是没有办法抛出到主线程中的。因此在throw时由于没有地方接收进行处理,会直接抛给操作系统导致程序崩溃。
}
}
private void ExAction(string err)
{
MessageBox.Show(err);
}
private void Callback(IAsyncResult ar)
{
string str=(ar.AsyncState as Func<string>).EndInvoke(ar);
this.timer1.Enabled = false;
time = 1;
this.label1.Text = str;
}
private void btnStart_Click(object sender, EventArgs e)
{
try
{
Func<string> func = LongTimeTask;
timer1.Enabled = true;
IAsyncResult result = func.BeginInvoke(Callback, func);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void timer1_Tick(object sender, EventArgs e)
{
label1.Text = "执行时间:" + time.ToString();
time++;
}
}
}
参考资料: