您的位置:首页 > 产品设计 > 产品经理

模式1、APM异步编程模型 Net1.0 [Asynchronous Programming Mode]

2016-11-20 17:45 344 查看
BeginXXX方法和EndXXX方法是这个编程模型的标识之一,看到这个是不是好像在哪见过?是的没有错,那就是委托的BeginXXX方法和EndXXX方法。(当然还有跨线程调用的BeginInvoke 这个可以另开一片文章来讨论了)

delegate int TestDelegate(int numA, int numB, out long ticks);
public static void Test3()
{
long ticks = 0;
TestDelegate testD = AddNum;

IAsyncResult result1 = testD.BeginInvoke(1, 100, out ticks, null, null);
//可能回引起主线程阻塞
testD.EndInvoke(out ticks, result1);

//回调中调用EndXXX不会引起主线程阻塞
IAsyncResult result2 = testD.BeginInvoke(1, 100, out ticks, (d) => { testD.EndInvoke(out ticks, d); }, null);
testD.EndInvoke(out ticks, result2);

}


看完代码是不是想问IAsyncResult 是啥?(主要作用就是提供阻塞功能和保存原委托的执行状态) 定义如下:

// 表示异步操作的状态。
[ComVisible(true)]
public interface IAsyncResult
{
// 获取用户定义的对象,它限定或包含关于异步操作的信息。
object AsyncState { get; }
// 获取用于等待异步操作完成的System.Threading.WaitHandle,待异步操作完成时获得信号。
WaitHandle AsyncWaitHandle { get; }
// 获取一个值,该值指示异步操作是否同步完成。
bool CompletedSynchronously { get; }
// 获取一个值,该值指示异步操作是否已完成。
bool IsCompleted { get; }
}


BeingXXX是什么套路?

public IAsyncResult BeginXXX(object[] parameters, AsyncCallback callback, object asyncState)
//parameters 原委托的所有参数
//callback 回调函数,当原委托执行完之后开始调用
//asyncState 用户自定义参数


EndXXX是什么套路?

EndXXX通常情况下只有一个参数那就是IAsyncResult,但是如果原委托中含有 out ref 修饰的参数 那么EndXXX也得带这个参数(要不怎么赋值)

总的来说把AMP这个过程就是把一个Delegate异步化的一个过程 ,了解了以上微软对Delegate的APM异步改造之路后 我们自己尝试对一个同步方法进行改造

首先我们改造的同步方法如下:

/// <summary>
/// 从numA累加倒numB
/// </summary>
/// <param name="numA">开始数</param>
/// <param name="numB">结束数</param>
/// <param name="ticks">耗时</param>
/// <returns>计算结果</returns>
public static int AddNum(int numA, int numB, out long ticks)
{
Stopwatch st = new Stopwatch();
st.Start();
int result = 0;
for (int i = numA; i < numB; i++)
{
Console.WriteLine(i);
result += i;
}
st.Stop();
ticks = st.ElapsedTicks;
return result;
}


改造第一步:(定义一个实现了IAsyncReuslt的类来实现 同步方法的状态的记录以及阻塞的实现)

/// <summary>
/// 一个类似仿照AsyncCallback的回调委托
/// </summary>
/// <param name="time"></param>
/// <param name="ar"></param>
public delegate void RefAsyncCallback(out long time, IAsyncResult ar);
public class CalculateAsyncResult : IAsyncResult
{
int numA, numB;
RefAsyncCallback userCallback;
public CalculateAsyncResult(int numA, int numB, RefAsyncCallback userCallback, object asyncState)
{
this.numA = numA;
this.numB = numB;
this.userCallback = userCallback;
this.asyncState = asyncState;
ThreadPool.QueueUserWorkItem((obj) => { AsyncCalculate(obj); }, this);
}
#region 接口实现部分

object asyncState;
ManualResetEvent asyncWaiHandle;
bool completedSynchronously;
bool isCompleted;

public object AsyncState
{
get
{
return asyncState;
}
}

public WaitHandle AsyncWaitHandle
{
get
{
if (asyncWaiHandle == null)
{
//用于EndXXX中的阻塞
ManualResetEvent event2 = new ManualResetEvent(false);

//如果asyncWaiHandle为空就给它赋值event2
Interlocked.CompareExchange(ref asyncWaiHandle, event2, null);
}
return asyncWaiHandle;
}
}

public bool CompletedSynchronously
{
get
{
return completedSynchronously;
}
}

public bool IsCompleted
{
get
{
return isCompleted;
}
}

#endregion

//最后结果
public int FinnalyResult { get; set; }

//endxx调用次数
public int EndCallCount = 0;

//out参数
public long ticks;

private static void AsyncCalculate(object obj)
{
CalculateAsyncResult asyncReuslt = obj as CalculateAsyncResult;

Stopwatch st = new Stopwatch();
st.Start();
int result = 0;
for (int i = asyncReuslt.numA; i < asyncReuslt.numB; i++)
{
Console.WriteLine(i);
result += i;
}
st.Stop();
asyncReuslt.ticks = st.ElapsedTicks;

asyncReuslt.completedSynchronously = false;
asyncReuslt.isCompleted = true;
(asyncReuslt.asyncWaiHandle as ManualResetEvent).Set();
//原来二元式还能这么用。。vs2015的小灯泡说的   asyncReuslt.userCallback不为空就调用  666
asyncReuslt.userCallback?.Invoke(out asyncReuslt.ticks, asyncReuslt);
}
}


改造第二步:(定义BeginXXX 和EndXXX)

public class CalculateLib
{
/// <summary>
/// 传说的BebinXXX
/// </summary>
/// <param name="numA">开始数</param>
/// <param name="numB">结束数</param>
/// <param name="userCallback">回调委托</param>
/// <param name="asyncState">自定义对象(此参数可以在IAsyncResult对象中获取到)</param>
/// <returns>返回一个可以获取原委托执行状态的对象</returns>
public IAsyncResult BeginCalculate(int numA, int numB, RefAsyncCallback userCallback, object asyncState)
{
return new CalculateAsyncResult(numA, numB, userCallback, asyncState);
}

/// <summary>
/// 传说中的EndXXX
/// </summary>
/// <param name="ticks">耗时</param>
/// <param name="ar">要End的IAsyncResult对象</param>
/// <returns>计算结果</returns>
public int EndCalculate(out long ticks, IAsyncResult ar)
{
CalculateAsyncResult result = ar as CalculateAsyncResult;
if (Interlocked.CompareExchange(ref result.EndCallCount, 1, 0) == 1)
{
throw new Exception("此方法只能调用一次");
}
//等着的开门放狗的信号
result.AsyncWaitHandle.WaitOne();
ticks = result.ticks;
return result.FinnalyResult;
}
}


改造完成开始i调用:

public class TestClass
{
#region 传统思维调用,可能导致主线程阻塞 从而界面假死
public static void Test1()
{
CalculateLib cal = new CalculateLib();
IAsyncResult calculateResult = cal.BeginCalculate(1, 100, null, null);
long times = 0;
int result = cal.EndCalculate(out times, calculateResult);
}
#endregion

#region 回调中调用EndXXX思维调用,不会导致主线程阻塞
public static void Test2()
{
CalculateLib cal = new CalculateLib();
IAsyncResult CalculateResult = cal.BeginCalculate(1, 100, null, cal);
}
public static void AfterCallback(out long tiems, IAsyncResult ar)
{
CalculateLib cal = ar.AsyncState as CalculateLib;
cal.EndCalculate(out tiems, ar);
}
#endregion

}


参考:http://www.cnblogs.com/heyuquan/archive/2013/03/22/Asynchronous-Programming-Model.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: