等待异步功能,然后返回

时间:2019-12-17 12:28:52

标签: c# asp.net asynchronous

我正在创建消息传递系统,但遇到了问题。我需要发布一条消息并等待响应,然后再返回发布功能。

这就是我的功能

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(string msg){
    // Intercepts all messages
    ...
}

这两个功能都在服务器上,并且每当发送一条消息(包括使用Publish方法发送的一条消息)时,MessageIntercept任务就会自动运行。我可以通过调用上述服务器项目的“发布”功能从asp.net网站项目中发送消息

基本上,我想做的是致电bool Success = Publish(1,"This is a test"),并能够确定邮件是否已成功发送,客户端可以在5秒钟内理解并恢复该邮件。

这是逐步发生的事情:

  1. 我使用Publish从服务器向设备发送一条消息
  2. 该消息被MessageIntercept方法拦截(我并不在意,但是代码被编写为可以拦截所有消息)
  3. 客户端接收并处理消息
  4. 客户端响应,并且消息在MessageIntercept中被拦截,这是我想在返回Publish方法之前验证消息的地方

5 个答案:

答案 0 :(得分:1)

您可以使用这样的帮助方法:

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();
}

答案 1 :(得分:1)

没有任何要等待的发布,因此您需要添加一个通知挂钩。像“ 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; }
}

答案 2 :(得分:0)

这是一个异步函数,如果结果返回,您将必须执行“某些操作”。例如,您可以填写一个隐藏字段值,然后再次从代码隐藏中读取它。或触发另一个异步函数并触发codebehind方法。 为了能够在响应后写点东西,您必须先听一下。

答案 3 :(得分:-1)

如果要同步运行异步函数,可以在System.Threading.Tasks库中使用以下代码

Task.Run(async () => await { async method here }).Result

这将导致您的异步方法被阻塞,并且您的代码将不会继续,直到您返回响应为止。

在您的异步方法中,您可以添加超时Thread.Sleep(5000)或在异步函数中对结果进行迭代(如果避免Thread.Sleep,则可能需要使用秒表)。

答案 4 :(得分:-2)

bool Success = await Publish(1,"This is a test")