• async...await在tcp通讯中的正确用法


    • 引言

        编程能力在不断的总结中进步以及成长,最近的半年里,对之前的开源项目代码进行回归,在重构的过程中进行了很多思考,很多次都想放弃重构,毕竟一个已经在使用的项目,重构基础代码就相当于重新开发了,不过最终还是下定了决心,毕竟重构就是一个成长过程,要想进步,就要不断的发现原有代码的不足,使用新的思维去优化原来的东西,在重构的过程中,针对tcp网络通讯,我有了新的思路。

    • 简介

        无论是网上的tcp示例或者书本的示例,只要是异步方式,读写基本都是分离的,也就是说如果想通过tcp去和服务端进行数据交互,你在A方法发送了指令,只能在B方法接收并进行处理,这种发送与接收数据不在同一个方法内,严重干扰了代码的顺序执行逻辑,我们需要将发送前和接收后的代码写在不同的地方,而现在,我们可以卸载一起。例如:
        

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    long fileId = 1001;
    // 通过tcp发送命令到服务端
    bool result = await tcp.Delete(fileId);
    // 返回结果后处理相应的业务
    if(result){
      // 刷新数据 
    }
    else{
      // 提示错误
    }

        没错,这样子看上去和调用http请求一模一样,是不是逻辑变得简单了?

    • 调用示例

        画一个时序图吧,我们来看一下调用过程

     

     

     

         从上图我们可以看出,和其它网上实例的不同在于,我们自己加了一个task管理模块,我们通过自己对Task进行管理,实现了tcp的异步调用但顺序执行代码。

        将以上示例用在实际代码上,效果如下:

     

     

     

     

    • 核心代码

        

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    ///
            /// 等待请求
            ///
            /// 请求的token
            /// 超时时间
            ///
            public async Task Wait(T1 token, TimeSpan timeOut)
            {
                TaskCompletionSource taskCompletionSource = new TaskCompletionSource();
                // 将等待结果的任务加入字典中
                TaskDict.Add(token, taskCompletionSource);
                try
                {
                    T ret = await taskCompletionSource.Task.WaitAsync(timeOut);
                    return ret;
                }
                catch
                {
                    // 如果超时,则移除字典中的任务,并抛出超时异常
                    if (TaskDict.ContainsKey(token))
                    {
                        TaskDict.Remove(token);
                    }
                    throw;
                }
            }   

      

    • 结尾

        async...await是异步方案的一种,配合TaskCompletionSource可以将分散于2个不同地方的代码合并到一起,对简化代码逻辑来说还是比较靠谱的。

        目前这种异步方案已在自己的开源项目(Wireboy.Socket.P2PSocket)中使用了,并且也将此方案适配在了公司项目,主要运用于使用命名管道的进程间通讯。总的来说还是一个比较不错的方案。

    • 适用场景
    1. tcp通讯
    2. udp通讯
    3. 命名管道通讯
    4. UI线程需要大量计算,将计算过程使用线程启动,并通过此方案返回结果。

     

  • 相关阅读:
    FITC荧光素标记琼脂糖Agarose,阿卓糖altrose,聚蔗糖Ficoll,鼠李糖Rhamnose等糖
    免备案海外服务器有什么好处?
    Oracle数据库中的table@xyz是什么意思?
    【PAT乙级】1015 德才论
    c++多态
    获取类对象的三种方法分享
    三个线程顺序打印ABC?我有十二种做法,彻底掌握多线程同步通信机制
    10.过拟合、欠拟合与正则化
    【matlab图像处理笔记4】【图像变换】(三)图像的霍夫变换
    Java带图片的excel数据导入
  • 原文地址:https://www.cnblogs.com/zhuxiaoxiao/p/16580117.html