异步委托 学习笔记
System.Windows.Forms.Timer 一直执行在主线程上(即UI线程上)
1、定义一个委托
public delegate int DoSomethingDelegate(int input);
2、定义一个类,类中的方法同委托匹配签名
public class MyObject
{
public int DoubleNumber(int input)
{
return input * 2;
}
}
3、创建一个委托,指向类的方法
MyObject myObj = new MyObject();
// Create a delegate that points to the myObj.DoubleNumber() method.
DoSomethingDelegate doSomething = new DoSomethingDelegate(myObj.DoubleNumber);
// Call the myObj.DoubleNumber() method through the delegate.
int doubleValue = doSomething(12);
What you may not realize is that delegates
直接调用委托方法是,实际使用的是Invoke方法,此方法同步执行相关函数。
另外还有一个BeginInvoke方法,用于异步执行相关函数
IAsyncResult async = doSomething.BeginInvoke(12, null, null);
但是并不返回函数的执行结果。 参数为原始参数+回调对象+状态对象。
前面的例子用异步方法重写后:
MyObject myObj = new MyObject();
// Create a delegate that points to the myObj.DoubleNumber() method.
DoSomethingDelegate doSomething = new DoSomethingDelegate(myObj.DoubleNumber);
// Start the myObj.DoubleNumber() method on another thread.
IAsyncResult async = doSomething.BeginInvoke(originalValue, null, null); //返回一个IAsyncResult对象。
// (Do something else here while myObj.DoubleNumber() is executing.)
// Retrieve the results, and wait (synchronously) if they're still not ready.
int doubleValue = doSomething.EndInvoke(async); //通过IAsyncResult对象获取值。
■
当你调用一个异步方法时,Clr调用了它的线程池中的线程来使用,这个线程池大小一般为单cpu,25个线程。
轮询和回调
当你调用EndInvoke()时,调用成为同步。就是说如果调用没有返回,程序需要等待。
可以使用IAsyncResult.IsCompleted属性来进行查询
IAsyncResult async = doSomething.BeginInvoke(12, null, null);
// Loop until the method is complete.
while (!async.IsCompleted)
{
// Do a small piece of work here.
}
int doubleValue = doSomething.EndInvoke(async);
效率不是很高。
一个更好的选择是使用回调方法。
//回调方法使用IAsyncResult对象作为参数
private void MyCallback(IAsyncResult async)
{ ... }
回调方法的使用
doSomething.BeginInvoke(12, new AsyncCallback(this.MyCallback), null);
回调方法不知道他是被谁触发的,就是说如果这个回调方法对应多个异步操作,它就不知道是哪个操作完成了。
为了取消这个限制,可以给BeginInvoke方法的最后一个参数赋值
然后通过 IAsyncResult.AsyncState 获取
一个有用的技巧是,是使用委托对象作为状态对象
doSomething.BeginInvoke(originalValue,
new AsyncCallback(this.MyCallback), doSomething);
回调方法如下:
private void MyCallback(IAsyncResult async)
{
// Retrieve the delegate.
DoSomethingDelegate doSomething = (DoSomethingDelegate)async.AsyncState;
// Use it to retrieve the result.
int doubleValue = doSomething.EndInvoke(async);
// (Do something with the retrieved information.)
}
回调方法同异步执行方法在同一个线程中,而不是在主线程(ui线程)中。
Windows窗体中的一个示例
代码1:
public class Worker
{
public static int[] FindPrimes(int fromNumber, int toNumber)
{
// Find the primes between fromNumber and toNumber,
// and return them as an array of integers.
}
}
代码2:
private void cmdFind_Click(object sender, EventArgs e)
{
this.UseWaitCursor = true;
txtResults.Text = "";
lblTimeTaken.Text = "";
// Get the search range.
int from, to;
if (!Int32.TryParse(txtFrom.Text, out from))
{
MessageBox.Show("Invalid From value.");
return;
}
if (!Int32.TryParse(txtTo.Text, out to))
{
MessageBox.Show("Invalid To value.");
return;
}
// Start the search for primes and wait.
DateTime startTime = DateTime.Now;
int[] primes = Worker.FindPrimes(from, to);
// Display the time for the call to complete.
lblTimeTaken.Text =
DateTime.Now.Subtract(startTime).TotalSeconds.ToString();
// Paste the list of primes together into one long string.
StringBuilder sb = new StringBuilder();
foreach (int prime in primes)
{sb.Append(prime.ToString());
sb.Append(" ");
}
txtResults.Text = sb.ToString();
this.UseWaitCursor = false;
}
此时,执行过程中,界面无法操作。
异步执行:
代码1:执行异步操作,并更新界面
private void CallAsyncWorker(int from, int to)
{
// Start the search for primes and wait.
DateTime startTime = DateTime.Now;
int[] primes = Worker.FindPrimes(from, to);
// Calculate the time for the call to complete.
TimeSpan timeTaken = DateTime.Now.Subtract(startTime);
// Paste the list of primes together into one long string.
StringBuilder sb = new StringBuilder();
foreach (int prime in primes)
{
sb.Append(prime.ToString());
sb.Append(" ");
}
// Use the Control.Invoke() method of the current form,
// which is owned by the same thread as the rest of the controls.
//在异步执行方法中直接更新结果,没有使用回调函数等。
this.Invoke(new UpdateFormDelegate(UpdateForm),
new object[] {timeTaken, sb.ToString()} );
}
代码2:
private delegate void CallAsyncWorkerDelegate(int from, int to);
代码3:
private void cmdFind_Click(object sender, EventArgs e)
{
// Disable the button.
cmdFind.Enabled = false;
txtResults.Text = "";
lblTimeTaken.Text = "";
// Get the search range.
int from, to;
if (!Int32.TryParse(txtFrom.Text, out from))
{
MessageBox.Show("Invalid From value.");
return;
}
if (!Int32.TryParse(txtTo.Text, out to))
{
MessageBox.Show("Invalid To value.");
return;
}
// Start the search for primes on another thread.
CallAsyncWorkerDelegate doWork = new
CallAsyncWorkerDelegate(CallAsyncWorker);
doWork.BeginInvoke(from, to, null, null);
}
代码4
private delegate void UpdateFormDelegate(TimeSpan timeTaken, string primeList);
代码5
private void UpdateForm(TimeSpan timeTaken, string primeList)
{
lblTimeTaken.Text = timeTaken.TotalSeconds.ToString();
txtResults.Text = primeList;
cmdFind.Enabled = true;
}