了解 .NET 中的等待和异步
Understanding await and async in .NET
我正在重构使用 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,但仍然要求我不要立即 return 返回给调用者。我该怎么做呢?这可能吗?由于 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?
}
您需要等待这两个任务尝试以下操作:
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?
}
//wait for BOOTP/Update to finish before returning to caller
您根本不需要任何异步 IO,因为您想等到所有操作都完成。我假设您已经复制了一些示例代码。大多数示例代码使用异步套接字 API。
将所有内容切换到同步套接字 API,您就完成了。
如果你出于某种原因想要保持异步,你确实可以切换到 await 并解开这段代码。您发布的伪代码看起来是个不错的目标。不过,它强制周围方法为 async Task
。
您也可以通过使所有调用者递归异步来解决这个问题。如果您不需要保留线程,您可以阻塞该任务并拥有一个大部分同步的调用链。不过,到那时您将失去所有异步优势。
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() 等待所有事件完成,以便方法本身 returns 同步。
我正在重构使用 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,但仍然要求我不要立即 return 返回给调用者。我该怎么做呢?这可能吗?由于 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?
}
您需要等待这两个任务尝试以下操作:
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?
}
//wait for BOOTP/Update to finish before returning to caller
您根本不需要任何异步 IO,因为您想等到所有操作都完成。我假设您已经复制了一些示例代码。大多数示例代码使用异步套接字 API。
将所有内容切换到同步套接字 API,您就完成了。
如果你出于某种原因想要保持异步,你确实可以切换到 await 并解开这段代码。您发布的伪代码看起来是个不错的目标。不过,它强制周围方法为 async Task
。
您也可以通过使所有调用者递归异步来解决这个问题。如果您不需要保留线程,您可以阻塞该任务并拥有一个大部分同步的调用链。不过,到那时您将失去所有异步优势。
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() 等待所有事件完成,以便方法本身 returns 同步。