异步等待属性访问

时间:2017-01-31 00:05:10

标签: c# sockets asynchronous async-await

我正在编写一个类似于这个的代码(我在stackoverflow上修改了它,所以可能它有拼写错误)我有些疑惑。

  1. 我知道向此听众发送PacketType.RenamePacketType.GetName 之后,它可能会结束,在更新GetName()上的值之前发送Rename()结果。我应该在任何方法之前在两个方法上使用锁定对象 逻辑确保"命令"?我可以假设BeginReceive()任务 在等待Rename()被调用之前,不会开始阅读下一条消息 因此,Rename()内的第一行应始终锁定 在代码从PacketType.GetName获取之前调用 NetworkStream,拨打执行并致电等待GetName()
  2. 同一个PacketType.Rename一起发送10次,将获得一个数据包 在BeginReceive()上按一个,运行Execute()然后返回以获取下一个 一旦代码等待Rename(),就可以使用Rename()方法 在多个任务中同时运行。如果我不在乎 哪一个是像这种情况更新值的最新一个(全部 重命名是相同的),或更新的值与a不相关 姓名,我应该关心吗?
  3. 如果我想在我的异步方法上使用服务/存储库模式 对数据库运行一些CRUD,而服务/存储库只使用局部变量和方法参数,在MockConnectionHandler作为注入属性的单个服务实例应该没问题?每个任务应该使这些独立于运行相同服务/存储库的其他任务吗?
  4. 我应该避免哪些明显的事情?
  5. public class Listener : IListener
    {
      public string Name {get;set;}
      public int Connected {get;set;}
    
      ...
      private async Task Listen()
      {
        while (!_Token.IsCancellationRequested)
        {
          tcpClient = await _Listener.AcceptTcpClientAsync().ConfigureAwait(false);
    
          Connected++;
          IConnectionHandler connectionHandler = _ClientPool.Receive();
          connectionHandler.TcpClient = tcpClient;
          connectionHandler.Listener = this;  // really is done on the Listener init()
          connectionHandler.Init();
        }
      }
    }
    
    public class MockConnectionHandler : IConnectionHandler 
    {
      public string Name {get;set;}
      public int Messages {get;set;}
      public IListener Listener {get;set;}
    
      public void Init()
      {
         BeginReceive();
      }
    
      private void BeginReceive()
      {
        var receive = Task.Run(async () =>
        {
          while (true)
          {
            readedHeader = await _Stream.ReadAsync(dataHead, 0, headerLength, _DisconnectToken);
            // get body length from header.
            readedBody = await _Stream.ReadAsync(dataBody, 0, bodyLength, _DisconnectToken);
    
            Task run = Task.Run(() => Execute(headType, dataBody));
          }
        }, _DisconnectToken)
        .ContinueWith(previous =>
        {
          _EndReceiving = true;
        }, TaskContinuationOptions.OnlyOnCanceled);
      }
    
      private async Task Execute(PacketType type, byte[] data)
      {
        switch(packetType)
        {
          case PacketType.Echo:
             await SendAsync(new Bag(PacketType.Echo));
          case PacketType.Rename:
             await Rename(); 
          case PacketType.GetName:
             await GetName();
        }
      }
    
      private async Task Rename(byte[] data)
      {
        Name = Encoding.UTF8.GetString(data);
        Listener.Name = Encoding.UTF8.GetString(data);
      }
    
      private async Task GetName()
      {
        byte[] data = Encoding.UTF8.GetBytes(Name);
        SendAsync(new Bag(PacketType.Echo, data));
      }
    }
    

2 个答案:

答案 0 :(得分:1)

如果你等待每个异步函数就像你正在做的那样,代码将同步运行"但是潜在地减少了延迟。如果你可以并行执行某些操作,那么等待每个异步方法都是不好的,而是保存Task句柄,稍后当你需要所有任务的结果时等待一系列任务。

即。如果你有asyncMeth1()和asyncMeth2(),并且它们不相互依赖/可以并行运行,你不应该单独等待每一个,而是保存Task对象并在以后当你真正需要时等待它们两者的结果。

答案 1 :(得分:0)

任何时候你在任务上调用等待代码都会返回给调用者。一旦任务完成,它就会发出信号,然后代码返回到那个时间点。

只有在您选择不对每项任务使用等待时,这才会变得棘手。如果您在没有等待的情况下运行任务,那么该任务将与您的代码并行运行,作为单独的线程。如果等待该任务,则返回调用者。

无论其;回答我认为你问...返回调用者不会在等待之前返回任务..它返回到链中的第一个等待调用。例如:如果你有一个等待某个任务的方法,那么该方法的调用者可以自由继续运行...在那里等待是否有另一个内部等待然后没有,你的第一个await不会启动并继续。这些任务像使用等待时的队列一样被链接在一起。

                → caller continues executing
caller → method → await task1 → await task2 → await task3 
                                                   ↓
                                             task3 complete
                                                   ↓
                                cont task2 ← -------
                                     ↓
                               task2 complete
                                     ↓
                   cont task1 ← ------
                        ↓
                 task1 complete
                        ↓
      cont method ← -----
           ↓
    method complete
           ↓
    (nothing to do)

值得一提的是,在任务中跳过await关键字的任何地方,它都会成为链中该点的调用者。