本文共 3190 字,大约阅读时间需要 10 分钟。
微软提供了大量的异步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 tcs1 = new TaskCompletionSource (); Task 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 tcs2 = new TaskCompletionSource (); Task 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 的等待不会堵塞线程池的当前线程,可以保证线程池的正常工作,应对大规模的并发。
转载地址:http://kmvad.baihongyu.com/