【问题标题】:Wait for a callback to be called in an async method [duplicate]等待在异步方法中调用回调[重复]
【发布时间】:2021-05-11 09:31:54
【问题描述】:

假设我有以下订阅事件的方法。事件发生时调用回调。我想阻止我的方法返回,直到调用回调,或者经过 10 秒。

public async Task<string> GetImportantString()
{
    string importantResult = null;
    await SubscribeToEvent("some event", async (message) =>
    {
        importantResult = message; // When "some event" happens, callback is called and we can set importantResult 
    }

    return message; // Only return when the callback is called, or 10 seconds have passed
}

SubscribeToEvent()的签名如下:

public Task SubscribeToEvent(string event, Action<string> handler);

我使用方法GetImportantString()的方式如下:

public void SomeMethod() 
{
    // Do some things
    var importantString = await GetImportantString();
   // Do other things
}

问题是在调用回调之前我找不到不从GetImportantString() 返回的方法。理想情况下,我想等到回调调用最多 10 秒,如果在 10 秒内未调用回调,则返回错误。如何在调用回调之前暂停GetImportantString() 的执行?

【问题讨论】:

  • GetImportantString 中异步的全部意义在于不阻塞调用者线程。通常,如果还等待,则仅在 SubscribeToEvent 返回后继续执行
  • SubscribeToEvent 方法的更好签名是:public Task&lt;string&gt; SubscribeToEvent(string event, Func&lt;string, Task&lt;string&gt;&gt; handler);。您当前的签名不允许与异步委托进行适当的合作。您示例中的async (message) =&gt; lambda 是async void,即something to avoid
  • 顺便说一句,async (message) =&gt; lambda 可能会在 Visual Studio 中生成警告,关于缺少 await 运算符的 async 方法。

标签: c# async-await


【解决方案1】:

看看这个:

public async Task<string> GetImportantString()
{
    string importantResult = null;
    using (var sph = new SemaphoreSlim(0, 1))
    {
        await SubscribeToEvent("some event", async (message) =>
        {
            importantResult = message; // When "some event" happens, callback is called and we can set importantResult 
            sph.Release();
        });

        var t = sph.WaitAsync();

        if (await Task.WhenAny(t, Task.Delay(10000)) == t)
        {
            return importantResult;
        }
    }
    throw new TimeoutException(); // whatever you want to do here
}

我们使用SemaphoreSlim 在字符串设置时发出信号。

此时,我们在 Task.WhenAny 上等待,它让我们知道信号量何时释放或延迟任务过去。如果 Semaphore 释放,我们可以安全地假设字符串已设置并返回。

【讨论】:

  • 感谢您的回复!不幸的是,SubscribeToEvent() 返回的Task 并不表示回调是否被调用,它只表示您是否订阅了该事件。目前,SubscribeToEvent 返回的Task 几乎立即解决,但实际回调在 5 秒后调用,因为事件需要更长的时间才能发生。
  • @ThomasFromUganda -- 试试 -- 我还没有测试过。
  • 我从未想到过这个解决方案。谢谢!
  • @TheodorZoulias -- 好消息 -- 我刚下床,我的头还没到正确的地方:) 谢谢!固定。
  • sph.Release(); 行可能会产生未处理的ObjectDisposedException
猜你喜欢
  • 2018-09-22
  • 2013-04-15
  • 2015-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多