本文主要是介绍.Net的异步机制(APM核心:IAsyncResult) - step 3,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
在上一篇文章(什么是.Net的异步机制(Invoke,BeginInvoke,EndInvoke) - step 2 ), 我们已经简单介绍了异步的调用方式, 下面我们来看看异步的核心.
异步的核心: IAsyncResult
Asynchronous Programming Model
整个异步调用过程中都是围绕IAsyncResult来进行的,大家可以看看上篇文章的例子,BeginXXX 返回这个对象,EndXXX接收这个对象来结束当前异步对象,下面我们来看看IAsyncResult 接口成员/和实现此接口的AsyncResult类成员(其中有些在上篇中已经涉及到)
IAsyncResult接口
1
public interface IAsyncResult
2
{
3
WaitHandle AsyncWaitHandle { get; } //阻塞一个线程,直到一个或多个同步对象接收到信号
4
Boolean IsCompleted { get; } //判读当前异步是否完成
5
Object AsyncState { get; } //获取额外的参数值,请看上一篇文章的Code 4.3
6
Boolean CompletedSynchronously { get; } //几乎没有使用
7
}
AsyncResult类
1
public class AsyncResult : IAsyncResult, IMessageSink
2
{
3
//IAsyncResult 的实现
4
public virtual WaitHandle AsyncWaitHandle { get; }
5
public virtual bool IsCompleted { get; }
6
public virtual object AsyncState { get; }
7
public virtual bool CompletedSynchronously { get; }
8
9
// 其他一些重要的属性
10
public bool EndInvokeCalled { get; set; } //检验是否调用了EndInvoke()
11
public virtual object AsyncDelegate { get; } //获取原始的委托对象,可查看上一篇文章中的Code 4.1/4.2/5
12
}
注意:基本上都是只读属性
下面我们来看看异步的执行顺序,并回顾下 IAsyncResult 下各个属性的应用,如果还是不熟悉请看前2篇文章.
Code 1:
1
class Program
2
{
3
static void Main(string[] args)
4
{
5
Console.WriteLine("[(#{1}){0}]:Asynchronous Start", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
6
7
AsyncTest test = new AsyncTest();
8
MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = test.YearlySalary;
9
//使用回调函数
10
AsyncCallback callback = new AsyncCallback(OnSalaryCallback);
11
IAsyncResult ar = del.BeginInvoke(100000, 15, 100000, callback, 2000);
12
13
DoAntherJob();
14
Console.ReadLine(); // 让黑屏等待,不会直接关闭..
15
}
16
17
//开始其他工作.
18
static void DoAntherJob()
19
{
20
Thread.Sleep(1000);//需要1秒才能完成这个工作,注1
21
Console.WriteLine("[(#{1}){0}]:Do Another Job", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
22
}
23
24
static void OnSalaryCallback(IAsyncResult asyncResult)
25
{
26
//通过AsyncState 获取额外的参数.
27
decimal para = (int)asyncResult.AsyncState;
28
29
//通过AsyncDelegate 获取原始的委托对象
30
AsyncResult obj = (AsyncResult)asyncResult;
31
MyThirdAsyncCode.AsyncTest.SalaryEventHandler del =
(MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;
32
33
if (asyncResult.IsCompleted)// 判读是否已经调用完成
34
Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
35
36
decimal val = del.EndInvoke(asyncResult);
37
38
Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val + para, Thread.CurrentThread.ManagedThreadId);
39
}
40
}
41 
42
public class AsyncTest
43
{
44
public delegate decimal SalaryEventHandler(decimal salary, int monthCount, decimal bonus); // 对应YearlySalary方法
45
public decimal YearlySalary(decimal salary, int monthCount, decimal bonus)
46
{
47
//模拟耗时/复杂的逻辑计算.
48
Thread.Sleep(3000);//等待3秒,注2
49
return salary * monthCount + bonus;
50
}
51
}

图1
我们看到DoAntherJob 比异步YearlySalary快2秒,看代码中(注1)和(注2),两个线程的执行结果
接下来,我们说说AsyncWaitHandle 属性. 他返回WaitHandle对象(System.Threading.WaitHandle), 他有3个重要的方法. WaitOne / WaitAny / WaitAll ,我们先来说下WaitOne,在Code1代码基础上只是增加了下面红色部分.
1,WaitOne
Code 1.1
IAsyncResult ar = del.BeginInvoke(100000, 15, 100000, callback, 2000);
//阻碍当前线程,直到异步调用结束.
ar.AsyncWaitHandle.WaitOne();
//开始其他工作.
DoAntherJob();

图1.1
执行输出,对比图1我们可以看到执行的次序不一样了(看时间),调用WaitOne,会阻碍当前线程,直到异步完成,才释放当前的线程, WaitOne 提供了时间的重载版本WaitOne(int millisecondsTimeout)/ WaitOne(TimeSpan timeout);来判断阻碍的时间.无参的版本是无限等待的(直到异步调用结束)
2, WaitAll
我们在Code1的代码基础上加上Hello的异步调用(使Main提供多个异步调用),注意红色部分.
Code 1.2
1
class Program
2
{
3
static void Main(string[] args)
4
{
5
Console.WriteLine("[(#{1}){0}]:Asynchronous Start", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
6
7
AsyncTest test = new AsyncTest();
8
MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = test.YearlySalary;
9
MyThirdAsyncCode.AsyncTest.AsyncEventHandler asy = test.Hello;
10
11
IAsyncResult salayAsyc = del.BeginInvoke(100000, 15, 100000, OnSalaryCallback, null);
12
IAsyncResult helloAsyc = asy.BeginInvoke("Hello Andy", OnHelloCallback, null);
13
//把所有异步的句柄保存到WaitHandle 对象中
14
WaitHandle[] handles = { salayAsyc.AsyncWaitHandle, helloAsyc.AsyncWaitHandle };
15
//阻碍当前线程,直到所有异步调用结束.
16
WaitHandle.WaitAll(handles);
17
18
//开始其他工作.
19
DoAntherJob();
20
Console.ReadLine(); // 让黑屏等待,不会直接关闭..
21
}
22
static void DoAntherJob()
23
{
24
Thread.Sleep(1000);//需要1秒才能完成这个工作,注1
25
Console.WriteLine("[(#{1}){0}]:Do Another Job", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
26
}
27
static void OnSalaryCallback(IAsyncResult asyncResult)
28
{
29
//通过AsyncDelegate 获取原始的委托对象
30
AsyncResult obj = (AsyncResult)asyncResult;
31
MyThirdAsyncCode.AsyncTest.SalaryEventHandler del =
(MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;
32
33
if (asyncResult.IsCompleted)// 判读是否已经调用完成
34
Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
35
36
decimal val = del.EndInvoke(asyncResult);
37
Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val, Thread.CurrentThread.ManagedThreadId);
38
}
39
40
static void OnHelloCallback(IAsyncResult asyncResult)
41
{
42
//通过AsyncDelegate 获取原始的委托对象
43
AsyncResult obj = (AsyncResult)asyncResult;
44
MyThirdAsyncCode.AsyncTest.AsyncEventHandler del =
(MyThirdAsyncCode.AsyncTest.AsyncEventHandler)obj.AsyncDelegate;
45
46
if (asyncResult.IsCompleted)// 判读是否已经调用完成
47
Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
48
49
string val = del.EndInvoke(asyncResult);
50
Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val, Thread.CurrentThread.ManagedThreadId);
51
}
52
}
53 
54
public class AsyncTest
55
{
56
public delegate decimal SalaryEventHandler(decimal salary, int monthCount, decimal bonus); // 对应YearlySalary方法
57
public delegate string AsyncEventHandler(string name); // 对应Hello 方法
58
public string Hello(string name)
59
{
60
//模拟耗时/复杂的逻辑计算.等待5秒
61
Thread.Sleep(5000);
62
return "Hello:" + name;
63
}
64
public decimal YearlySalary(decimal salary, int monthCount, decimal bonus)
65
{
66
//模拟耗时/复杂的逻辑计算.
67
Thread.Sleep(3000);//等待3秒
68
return salary * monthCount + bonus;
69
}
70
}

图1.2
从图1.2中可以看出,WaitAll会阻碍当前线程(主线程#10),等待所有异步的对象都执行完毕(耗时最长的异步),才释放当前的线程,WaitAll/WaitAny的重载版本和WaitOne一样.
3, WaitAny
和WaitAll 基本上是一样的.我们可以使用 WaitAny 来指定某个/某几个委托先等待,修改Code1.2红色部分,使用WaitAny.
Code1.3
//把salayAsyc异步的句柄保存到WaitHandle 对象中
WaitHandle[] handles = { salayAsyc.AsyncWaitHandle };
//阻碍当前线程,直到所有异步调用结束.
WaitHandle.WaitAny(handles);

图1.3
我们阻碍了DoAntherJob(#10)线程,直到Salary异步调用计算完成.同样我们可以巧用这三个方法来改变我们方法执行的顺序.
释放资源
Code2
1
static void OnSalaryCallback(IAsyncResult asyncResult)
2
{
3
//通过AsyncDelegate 获取原始的委托对象
4
AsyncResult obj = (AsyncResult)asyncResult;
5
MyThirdAsyncCode.AsyncTest.SalaryEventHandler del =
(MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;
6
7
decimal val = del.EndInvoke(asyncResult);
8
asyncResult.AsyncWaitHandle.Close();//显示的释放资源
9
}
当开始调用BeginXXX后,就会创建一个新的AsyncResult对象.这个对象会构造一个WaitHandle句柄(通过AsyncWaitHandle访问),当我们EndXXX后,并不会马上关闭这个句柄,而是等待垃圾收集器来关闭,这时候我们最后在调用EndXXX完成后,显示的关闭这个句柄.
说到这里,我们基本上把异步方法都解释一遍,下面我们来看看重构的异步对象,我们也可以细细体会异步对象的内部执行代码..下面Code3.1/3.2/3.3代码来自Jeffery Richard大师的Power Threading类库,具体可查看http://msdn.microsoft.com/en-us/magazine/cc163467.aspx
重构的异步对象
1步,构造一个内部无参的AsyncResultNoResult对象,继承IAsyncResult接口(保留原创的注释)
Code3.1
1
internal class AsyncResultNoResult : IAsyncResult
2
{
3
// Fields set at construction which never change while
4
// operation is pending
5
private readonly AsyncCallback m_AsyncCallback;
6
private readonly Object m_AsyncState;
7
8
// Fields set at construction which do change after
9
// operation completes
10
private const Int32 c_StatePending = 0;
11
private const Int32 c_StateCompletedSynchronously = 1;
12
private const Int32 c_StateCompletedAsynchronously = 2;
13
private Int32 m_CompletedState = c_StatePending;
14
15
// Field that may or may not get set depending on usage
16
private ManualResetEvent m_AsyncWaitHandle;
17
18
// Fields set when operation completes
19
private Exception m_exception;
20
21
public AsyncResultNoResult(AsyncCallback asyncCallback, Object state)
22
{
23
m_AsyncCallback = asyncCallback;
24
m_AsyncState = state;
25
}
26
27
public void SetAsCompleted(
28
Exception exception, Boolean completedSynchronously)
29
{
30
// Passing null for exception means no error occurred.
31
// This is the common case
32
m_exception = exception;
33
34
// The m_CompletedState field MUST be set prior calling the callback
35
Int32 prevState = Interlocked.Exchange(ref m_CompletedState,
36
completedSynchronously ? c_StateCompletedSynchronously :
37
c_StateCompletedAsynchronously);
38
if (prevState != c_StatePending)
39
throw new InvalidOperationException(
40
"You can set a result only once");
41
42
// If the event exists, set it
43
if (m_AsyncWaitHandle != null) m_AsyncWaitHandle.Set();
44
45
// If a callback method was set, call it
46
if (m_AsyncCallback != null) m_AsyncCallback(this);
47
}
48
49
public void EndInvoke()
50
{
51
// This method assumes that only 1 thread calls EndInvoke
52
// for this object
53
if (!IsCompleted)
54
{
55
// If the operation isn't done, wait for it
56
AsyncWaitHandle.WaitOne();
57
AsyncWaitHandle.Close();
58
m_AsyncWaitHandle = null; // Allow early GC
59
}
60
61
// Operation is done: if an exception occured, throw it
62
if (m_exception != null) throw m_exception;
63
}
64
65
Implementation of IAsyncResult
115
}
2步,继承AsyncResultNoResult对象,并且为他提供返回值和泛型的访问
Code3.2
1
internal class AsyncResult < TResult > : AsyncResultNoResult
2
{
3
// Field set when operation completes
4
private TResult m_result = default(TResult);
5
6
public AsyncResult(AsyncCallback asyncCallback, Object state) :
7
base(asyncCallback, state) { }
8
9
public void SetAsCompleted(TResult result,
10
Boolean completedSynchronously)
11
{
12
// Save the asynchronous operation's result
13
m_result = result;
14
15
// Tell the base class that the operation completed
16
// sucessfully (no exception)
17
base.SetAsCompleted(null, completedSynchronously);
18
}
19
20
new public TResult EndInvoke()
21
{
22
base.EndInvoke(); // Wait until operation has completed
23
return m_result; // Return the result (if above didn't throw)
24
}
25
}
3步,模拟长时间的异步工作
Code3.3
1
internal sealed class LongTask
2
{
3
private Int32 m_ms; // Milliseconds;
4
5
public LongTask(Int32 seconds)
6
{
7
m_ms = seconds * 1000;
8
}
9
10
// Synchronous version of time-consuming method
11
public DateTime DoTask()
12
{
13
Thread.Sleep(m_ms); // Simulate time-consuming task
14
return DateTime.Now; // Indicate when task completed
15
}
16
17
// Asynchronous version of time-consuming method (Begin part)
18
public IAsyncResult BeginDoTask(AsyncCallback callback, Object state)
19
{
20
// Create IAsyncResult object identifying the
21
// asynchronous operation
22
AsyncResult<DateTime> ar = new AsyncResult<DateTime>(
23
callback, state);
24
25
// Use a thread pool thread to perform the operation
26
ThreadPool.QueueUserWorkItem(DoTaskHelper, ar);
27
28
return ar; // Return the IAsyncResult to the caller
29
}
30
31
// Asynchronous version of time-consuming method (End part)
32
public DateTime EndDoTask(IAsyncResult asyncResult)
33
{
34
// We know that the IAsyncResult is really an
35
// AsyncResult<DateTime> object
36
AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;
37
38
// Wait for operation to complete, then return result or
39
// throw exception
40
return ar.EndInvoke();
41
}
42
43
// Asynchronous version of time-consuming method (private part
44
// to set completion result/exception)
45
private void DoTaskHelper(Object asyncResult)
46
{
47
// We know that it's really an AsyncResult<DateTime> object
48
AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;
49
try
50
{
51
// Perform the operation; if sucessful set the result
52
DateTime dt = DoTask();
53
ar.SetAsCompleted(dt, false);
54
}
55
catch (Exception e)
56
{
57
// If operation fails, set the exception
58
ar.SetAsCompleted(e, false);
59
}
60
}
61
}
来自Jeffrey Richter大师更多更详细的异步操作方法, 请查看http://www.wintellect.com/PowerThreading.aspx,对于一些朋友可能看不懂Code3.1-3.3代码(其实没什么所谓的),因为涉及到过多的线程知识,这里出于让你获得更多的更深层次的(异步)认识为目的,才提供上面代码,在以后的文章会再次探讨.
下一篇章中,我们来看看微软提供有异步调用的类是如何调用的,并从中我会给出些真实应用环境中的一些小技巧,让你编写的代码更健壮更完善.
这篇关于.Net的异步机制(APM核心:IAsyncResult) - step 3的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!