在返回之前等待异步函数
Wait for Async function before returning
我正在创建消息系统,但遇到了问题。在 return 使用发布功能之前,我需要发布消息并等待响应。
这就是我的函数的样子
public async Task<bool> Publish(int ClientId, string msg){
...
// Wait and check if the client the message was sent to respond
// if that does not happen within 5 seconds, return false, else true
}
private async Task MessageIntercept(int ClientId, string msg){
// Intercepts all messages
...
}
这两个函数都在服务器上,只要发送消息(包括使用 Publish 方法发送的消息),MessageIntercept 任务就会自动 运行。我可以通过调用上面提到的服务器项目的发布功能从我的 asp.net 网站项目发送消息
基本上我想要做的是调用 bool Success = Publish(1,"This is a test")
并能够确定消息是否已成功发送,客户端是否理解并在 5 秒内恢复消息。
这是一步一步发生的事情:
- 我使用
Publish
从服务器向设备发送消息
- 消息被
MessageIntercept
方法拦截(这个我不太在意,但是代码是这样写的,所有消息都被拦截了)
- 客户端接收并处理消息
- 客户端响应,消息在
MessageIntercept
中被拦截,这是我想在 returning Publish
方法 之前验证消息的地方
示例消息;
服务器消息:
{
ClientId: 13,
msg: "Hello World"
}
客户响应:
{
ClientId: 13,
msg: "{Success: true}"
}
MessageIntercept
拦截所有消息,包括刚刚发送的请求,由于它是请求而不是响应,因此应该被忽略。但是,一旦客户端响应一条消息,我想告诉 Publish 方法响应已成功完成,然后 return 为真。否则,如果客户端在 5 秒内没有响应,它应该假定为 false。
bool Success = await Publish(1,"This is a test")
这是一个异步函数,如果返回结果,您将不得不执行 "something"。例如,您可以填写一个隐藏字段值并再次从代码隐藏中读取它。或者触发另一个异步函数并触发代码隐藏方法。
为了能够在响应后写一些东西,你必须听它。
您可以使用这样的辅助方法:
public static async Task<bool> WaitFor(Task task, TimeSpan timeout)
{
return await Task.WhenAny(task, Task.Delay(timeout)) == task;
}
示例用法:
using System;
using System.Threading.Tasks;
namespace Demo
{
public class Program
{
public static async Task Main()
{
if (await WaitFor(MyAsyncMethod(), TimeSpan.FromSeconds(1)))
Console.WriteLine("First await completed");
else
Console.WriteLine("First await didn't complete");
if (await WaitFor(MyAsyncMethod(), TimeSpan.FromSeconds(3)))
Console.WriteLine("Second await completed");
else
Console.WriteLine("Second await didn't complete");
}
public static async Task MyAsyncMethod()
{
await Task.Delay(2000);
}
public static async Task<bool> WaitFor(Task task, TimeSpan timeout)
{
return await Task.WhenAny(task, Task.Delay(timeout)) == task;
}
}
}
对于您的 Publish()
方法,调用可能如下所示:
if (await WaitFor(Publish(1, "msg"), TimeSpan.FromSeconds(5)))
...
但是,请注意,使用此方法的缺点是,如果超过超时,则不会观察到任务抛出的任何异常。
如果您需要处理在您放弃等待任务后可能发生的任何异常,您可以像这样传递一个异常处理委托:
public static async Task<bool> WaitFor(Task task, TimeSpan timeout, Action<Exception> handleException)
{
var wrapperTask = task.ContinueWith(
t => handleException(t.Exception.InnerException),
TaskContinuationOptions.OnlyOnFaulted);
return await Task.WhenAny(wrapperTask, Task.Delay(timeout)) == task;
}
那么你可以这样称呼它:
public static async Task Main()
{
if (await WaitFor(
MyAsyncMethod(),
TimeSpan.FromSeconds(1),
exception => Console.WriteLine("Exception: " + exception.Message))
)
Console.WriteLine("First await completed");
else
Console.WriteLine("First await didn't complete");
Console.ReadLine();
}
Publish 没有等待的东西,所以你需要添加一个通知挂钩。像 "OnMessageIntercept" 这样的事件对我来说很有意义。
然后您可以等待将通过调用通知挂钩完成的任务。
public async Task<bool> PublishAsync(int clientId, string msg)
{
// Wait and check if the client the message was sent to respond
// if that does not happen within 5 seconds, return false, else true
var messageRecievedSource = new TaskCompletionSource<int>();
void intercept(object sender, MessageInterceptEventArgs args)
{
if (args.ClientId == clientId)
messageRecievedSource.SetResult(clientId);
}
OnMessageIntercept += intercept;
// EDIT
// var completed = Task.WaitAny(Task.Delay(TimeSpan.FromSeconds(5)), messageRecievedSource.Task) > 0;
var completed = await Task.WhenAny(Task.Delay(TimeSpan.FromSeconds(5)), messageRecievedSource.Task);
OnMessageIntercept -= intercept;
// EDIT
// return completed;
return completed == messageRecievedSource.Task;
}
event EventHandler<MessageInterceptEventArgs> OnMessageIntercept;
private async Task MessageIntercept(int clientId, string msg)
{
OnMessageIntercept?.Invoke(this, new MessageInterceptEventArgs(clientId, msg));
// Intercepts all messages
}
class MessageInterceptEventArgs
{
public MessageInterceptEventArgs(int clientId, string msg)
{
ClientId = clientId;
Msg = msg ?? throw new ArgumentNullException(nameof(msg));
}
public int ClientId { get; }
public string Msg { get; }
}
如果你想运行一个同步的异步函数你可以在System.Threading.Tasks
库中使用下面的代码
Task.Run(async () => await { async method here }).Result
这将导致您的异步方法变成阻塞状态,并且您的代码将不会继续,直到您收到响应。
在您的异步方法中,您可以添加超时 Thread.Sleep(5000)
或在异步函数中迭代结果(如果您避免 Thread.Sleep,可以使用秒表)。
我正在创建消息系统,但遇到了问题。在 return 使用发布功能之前,我需要发布消息并等待响应。
这就是我的函数的样子
public async Task<bool> Publish(int ClientId, string msg){
...
// Wait and check if the client the message was sent to respond
// if that does not happen within 5 seconds, return false, else true
}
private async Task MessageIntercept(int ClientId, string msg){
// Intercepts all messages
...
}
这两个函数都在服务器上,只要发送消息(包括使用 Publish 方法发送的消息),MessageIntercept 任务就会自动 运行。我可以通过调用上面提到的服务器项目的发布功能从我的 asp.net 网站项目发送消息
基本上我想要做的是调用 bool Success = Publish(1,"This is a test")
并能够确定消息是否已成功发送,客户端是否理解并在 5 秒内恢复消息。
这是一步一步发生的事情:
- 我使用
Publish
从服务器向设备发送消息
- 消息被
MessageIntercept
方法拦截(这个我不太在意,但是代码是这样写的,所有消息都被拦截了) - 客户端接收并处理消息
- 客户端响应,消息在
MessageIntercept
中被拦截,这是我想在 returningPublish
方法 之前验证消息的地方
示例消息;
服务器消息:
{
ClientId: 13,
msg: "Hello World"
}
客户响应:
{
ClientId: 13,
msg: "{Success: true}"
}
MessageIntercept
拦截所有消息,包括刚刚发送的请求,由于它是请求而不是响应,因此应该被忽略。但是,一旦客户端响应一条消息,我想告诉 Publish 方法响应已成功完成,然后 return 为真。否则,如果客户端在 5 秒内没有响应,它应该假定为 false。
bool Success = await Publish(1,"This is a test")
这是一个异步函数,如果返回结果,您将不得不执行 "something"。例如,您可以填写一个隐藏字段值并再次从代码隐藏中读取它。或者触发另一个异步函数并触发代码隐藏方法。 为了能够在响应后写一些东西,你必须听它。
您可以使用这样的辅助方法:
public static async Task<bool> WaitFor(Task task, TimeSpan timeout)
{
return await Task.WhenAny(task, Task.Delay(timeout)) == task;
}
示例用法:
using System;
using System.Threading.Tasks;
namespace Demo
{
public class Program
{
public static async Task Main()
{
if (await WaitFor(MyAsyncMethod(), TimeSpan.FromSeconds(1)))
Console.WriteLine("First await completed");
else
Console.WriteLine("First await didn't complete");
if (await WaitFor(MyAsyncMethod(), TimeSpan.FromSeconds(3)))
Console.WriteLine("Second await completed");
else
Console.WriteLine("Second await didn't complete");
}
public static async Task MyAsyncMethod()
{
await Task.Delay(2000);
}
public static async Task<bool> WaitFor(Task task, TimeSpan timeout)
{
return await Task.WhenAny(task, Task.Delay(timeout)) == task;
}
}
}
对于您的 Publish()
方法,调用可能如下所示:
if (await WaitFor(Publish(1, "msg"), TimeSpan.FromSeconds(5)))
...
但是,请注意,使用此方法的缺点是,如果超过超时,则不会观察到任务抛出的任何异常。
如果您需要处理在您放弃等待任务后可能发生的任何异常,您可以像这样传递一个异常处理委托:
public static async Task<bool> WaitFor(Task task, TimeSpan timeout, Action<Exception> handleException)
{
var wrapperTask = task.ContinueWith(
t => handleException(t.Exception.InnerException),
TaskContinuationOptions.OnlyOnFaulted);
return await Task.WhenAny(wrapperTask, Task.Delay(timeout)) == task;
}
那么你可以这样称呼它:
public static async Task Main()
{
if (await WaitFor(
MyAsyncMethod(),
TimeSpan.FromSeconds(1),
exception => Console.WriteLine("Exception: " + exception.Message))
)
Console.WriteLine("First await completed");
else
Console.WriteLine("First await didn't complete");
Console.ReadLine();
}
Publish 没有等待的东西,所以你需要添加一个通知挂钩。像 "OnMessageIntercept" 这样的事件对我来说很有意义。
然后您可以等待将通过调用通知挂钩完成的任务。
public async Task<bool> PublishAsync(int clientId, string msg)
{
// Wait and check if the client the message was sent to respond
// if that does not happen within 5 seconds, return false, else true
var messageRecievedSource = new TaskCompletionSource<int>();
void intercept(object sender, MessageInterceptEventArgs args)
{
if (args.ClientId == clientId)
messageRecievedSource.SetResult(clientId);
}
OnMessageIntercept += intercept;
// EDIT
// var completed = Task.WaitAny(Task.Delay(TimeSpan.FromSeconds(5)), messageRecievedSource.Task) > 0;
var completed = await Task.WhenAny(Task.Delay(TimeSpan.FromSeconds(5)), messageRecievedSource.Task);
OnMessageIntercept -= intercept;
// EDIT
// return completed;
return completed == messageRecievedSource.Task;
}
event EventHandler<MessageInterceptEventArgs> OnMessageIntercept;
private async Task MessageIntercept(int clientId, string msg)
{
OnMessageIntercept?.Invoke(this, new MessageInterceptEventArgs(clientId, msg));
// Intercepts all messages
}
class MessageInterceptEventArgs
{
public MessageInterceptEventArgs(int clientId, string msg)
{
ClientId = clientId;
Msg = msg ?? throw new ArgumentNullException(nameof(msg));
}
public int ClientId { get; }
public string Msg { get; }
}
如果你想运行一个同步的异步函数你可以在System.Threading.Tasks
库中使用下面的代码
Task.Run(async () => await { async method here }).Result
这将导致您的异步方法变成阻塞状态,并且您的代码将不会继续,直到您收到响应。
在您的异步方法中,您可以添加超时 Thread.Sleep(5000)
或在异步函数中迭代结果(如果您避免 Thread.Sleep,可以使用秒表)。