微软提供了大量的异步API供我们使用,比如网络api,http协议的api等等,十分方便。在熟练掌握异步编程之后,我尝试自己写一个异步库,主要是tcp网络通信方面的,协议是私有协议,我在私有协议的基础上再封装一层供应用层调用。
但是我发现问题没那么简单,其他的都比较简单,task 之类,难点在一个地方:
请求------------------------------》网络等待----------------------------》网络应答-----------------------》协议解析---------------应用层处理
这里有一个等待的过程,我需要等待 等待 再等待。如何去等待?
开始我想到了信号量,于是就如下代码:
if(msg._autoResetEvent.WaitOne(m_iWaitTime))
{
if (GetResult(msg.Seq, out ShamResponseMsg outMsg))
{
sg.FromIp = outMsg.FromIp;
sg.FromPort = outMsg.FromPort;
sg.Result = outMsg.Result;
sg.Xml = outMsg.Xml;
sg.MsgId = outMsg.MsgId;
}
else
{
sg.Result = ShamResult.NoResult;
}
}
等待网络应答完,在接下来处理。开始用着来也没觉得有什么异常。
某天,大规模并发过来之后,发现我的等待把net的线程池阻塞住了,其他依赖线程池的功能都无法正常工作了。
我表示难道就没有其他的好的等待的方法么?只能用信号量吗?幸好,net4.5是开源的,我想去看看他内部如何使用http的异步的,终于被我找到了谜底。
TaskCompletionSource TaskCompletionSource TaskCompletionSource 这个类就是微软为我们创造出来的异步等待,他不会堵塞当前线程,等收到应答后,又能自动切换回来。
接下来,就研究这个类如何使用,我就抄一段msdn上面的例子,供大家学习和参考。
static void Main()
{
TaskCompletionSource<int> tcs1 = new TaskCompletionSource<int>();
Task<int> t1 = tcs1.Task;
// Start a background task that will complete tcs1.Task
Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
tcs1.SetResult(15);
});
// The attempt to get the result of t1 blocks the current thread until the completion source gets signaled.
// It should be a wait of ~1000 ms.
Stopwatch sw = Stopwatch.StartNew();
int result = t1.Result;
sw.Stop();
Console.WriteLine("(ElapsedTime={0}): t1.Result={1} (expected 15) ", sw.ElapsedMilliseconds, result);
// ------------------------------------------------------------------
// Alternatively, an exception can be manually set on a TaskCompletionSource.Task
TaskCompletionSource<int> tcs2 = new TaskCompletionSource<int>();
Task<int> t2 = tcs2.Task;
// Start a background Task that will complete tcs2.Task with an exception
Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
tcs2.SetException(new InvalidOperationException("SIMULATED EXCEPTION"));
});
// The attempt to get the result of t2 blocks the current thread until the completion source gets signaled with either a result or an exception.
// In either case it should be a wait of ~1000 ms.
sw = Stopwatch.StartNew();
try
{
result = t2.Result;
Console.WriteLine("t2.Result succeeded. THIS WAS NOT EXPECTED.");
}
catch (AggregateException e)
{
Console.Write("(ElapsedTime={0}): ", sw.ElapsedMilliseconds);
Console.WriteLine("The following exceptions have been thrown by t2.Result: (THIS WAS EXPECTED)");
for (int j = 0; j < e.InnerExceptions.Count; j++)
{
Console.WriteLine("\n-------------------------------------------------\n{0}", e.InnerExceptions[j].ToString());
}
}
//tt5();
Console.ReadKey();
}
重点总结:
使用这个类的TaskCompletionSource 的等待不会堵塞线程池的当前线程,可以保证线程池的正常工作,应对大规模的并发。