C#之异步编程

微软提供了大量的异步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 的等待不会堵塞线程池的当前线程,可以保证线程池的正常工作,应对大规模的并发。 

猜你喜欢

转载自blog.csdn.net/g0415shenw/article/details/81120480