【问题标题】:Understanding await and async in .NET了解 .NET 中的等待和异步
【发布时间】:2016-06-18 11:41:34
【问题描述】:

我正在重构代码,使用 Bootstrap 协议更新机器中多个节点的固件。当前代码看起来像这样(伪代码):

public void StartUpdate()
{
    Sokcet bootpSocket = new Socket():
    StateObject bootpState = new StateObject(bootpSocket);

    BOOTPReceive(bootpState);
    SendMagicPacket();

    while (!IsError && !IsUpdateComplete)
    {
        //wait for BOOTP/Update to finish before returning to caller
    }
}

private void BOOTPReceive(object state)
{
    bOOTPSocket.BeginReceive(PACKET_DATA, 0, PACKET_DATA.Length, 0, OnBOOTPReceive, state);
}

SendMagicPacket()
{
    //create and send magic packet
    // this will tell the node to respond with a BOOTPPacket
}

private void OnBOOTPReceive(IAsyncResult result)
{
    StateObject state = (StateObject) result.AsyncState;
    Socket handler = state.workSocket;
    int bytesRcvd = handler.EndReceive(result);
    packet = PACKET_DATA;

    if(isValidBOOTP(packet))
    {
        SendBOOTPResponse();    
    }
    else{
        BOOTPReceive(); //keep listening for valid bootp response
    }
}

private void SendBOOTPResponse()
{
    UdpClient udpClient = new UdpClient();
    udpClient.BeginSend(packetData, packetData.Length, BROADCAST_IP, (int)UdpPort.BOOTP_CLIENT_PORT, OnBOOTPSend, udpClient);
}

private void OnBOOTPSend(IAsyncResult result)
{
    UdpClient udpClient = (UdpClient)result.AsyncState;
    int bytesSent = udpClient.EndSend(result);
    udpClient.Close();
}

我想要做的是将其转换为 async-await 但仍然要求我不要立即返回给调用者。我该怎么做呢?这可能吗?由于 await-async 一直传播到顶部,这是否是正确的做法?

我认为这看起来像的伪代码:

public void StartUpdate()
{
    bool result = await SendMagicPacket();
    bool IsError = await BOOTPCommunication(); //Handles all of the BOOTP recieve/sends
    //don't return to caller until BOOTPCommunication is completed. How do i do this?
}

【问题讨论】:

    标签: c# async-await c#-5.0


    【解决方案1】:

    Radin 是在正确的轨道上,但我认为你想要的是这样的:

    您需要等待这两个任务尝试以下操作:

    public async Task StartUpdate()
    {
        var resultTask =  SendMagicPacket();
        var isErrorTask = BOOTPCommunication(); //Handles all of the BOOTP recieve/sends
    
        Task.WhenAll(new[]{resultTask, isErrorTask}).Wait(); //Wait() will block so that the method doesn't return to the caller until both of the asynchronous tasks complete.
    }
    

    允许 SendMagicPacket 和 BOOTPCommunication 同时触发,但等待两者完成。使用该模式,您可以同时触发 N 个事件,同时使用 Wait() 等待所有事件完成,以便方法本身同步返回。

    【讨论】:

      【解决方案2】:

      //等待BOOTP/Update完成后再返回调用者

      您根本不需要任何异步 IO,因为您想等到所有操作都完成。我假设您已经复制了一些示例代码。大多数示例代码使用异步套接字 API。

      将所有内容切换到同步套接字 API,您就完成了。

      如果您出于某种原因想要保持此异步,您确实可以切换到等待并解开此代码。您发布的伪代码看起来是一个不错的目标。但是,它强制周围的方法为async Task

      您也可以通过使所有调用者递归异步来处理这个问题。如果您不需要保留线程,则可以阻止该任务并具有大部分同步的调用链。不过,此时您将失去所有异步优势。

      【讨论】:

      • 你是对的,我一直在想这一切都错了。这都是同步的。我不知道为什么最后一个人异步执行此操作。
      【解决方案3】:

      您需要等待这两个任务尝试以下操作:

      public async Task StartUpdate()
          {
              var resultTask =  SendMagicPacket();
              var isErrorTask = BOOTPCommunication(); //Handles all of the BOOTP recieve/sends
      
              await Task.WhenAll(new[]{resultTask, isErrorTask});
              //don't return to caller until BOOTPCommunication is completed. How do i do this?
          }
      

      【讨论】:

      • 这并不能解决我的问题。 Await 仍会立即返回 StartUpdates() 调用者。
      • @stackErr,await 不会返回给StartUpdate 方法的调用者。它只是通知 CLR 异步执行代码并释放线程。
      猜你喜欢
      • 1970-01-01
      • 2014-01-09
      • 1970-01-01
      • 1970-01-01
      • 2013-11-13
      • 1970-01-01
      • 1970-01-01
      • 2014-12-09
      • 1970-01-01
      相关资源
      最近更新 更多