.NET 4.0异步等待跨线程事件

时间:2018-11-29 18:53:07

标签: c# .net multithreading

我有一个.NET服务,该服务具有2个API调用。这是我的情况:

  • 调用API 1时,服务将向客户端应用发送推送通知,并开始ASYNC等待;
  • 客户端应用收到推送通知,将立即调用API 2;
  • 一旦调用了API 2,API 2应该通知API 1的等待线程,以便API 1继续执行。

我想获得一些帮助或建议,以使API 1异步等待来自API 2的信号(在另一个线程上)是正确的技术选择。

(理想情况下仅使用.net库,而不使用外部库。)

2 个答案:

答案 0 :(得分:0)

我认为方程式的缺失部分是状态和唯一标识符。 API 1将带有标识符的响应推送到客户端后,而不是等待,您应该开始监视状态存储,以有意义的时间间隔进行轮询(我不知道延迟时间,所以可能是一秒钟,一分钟,或更多),以查找您刚刚发送的标识符。

客户端接收推送并使用标识符调用API 2。 API 2接收带有唯一令牌的调用,该令牌只有API 1才能生成,并且专用于此系列调用。 API 2完成工作后,会将唯一标识符和结果推送到状态存储中。 API 2退出,该线程被杀死。

与此同时,API 1一直在使用其唯一的标识符轮询状态存储,现在可以看到数据可用。退出等待状态,然后开始处理新发现的数据。

可以在内存中,基于文件的数据库中完成,也可以与云服务结合使用。但是总体架构不是直接控制CPU线程,而是沿着服务总线进行通信,并允许各方发出/订阅各种事件并确定何时响应/做出反应。

答案 1 :(得分:0)

您可以使用ConcurrentDictionarySemaphoreSlim

private static readonly ConcurrentDictionary<Guid, SemaphoreSlim> SyncItems = new ConcurrentDictionary<Guid, SemaphoreSlim>();

[HttpGet("api1")]
public async Task<IActionResult> Api1()
{
    Guid id = Guid.NewGuid();
    TimeSpan timeout = TimeSpan.FromSeconds(30);
    using (var semaphore = new SemaphoreSlim(0, 1))
    {
        if (SyncItems.TryAdd(id, semaphore))
        {
            try
            {
                await SignalClientAsync(id);
                if (await semaphore.WaitAsync(timeout))
                {
                    await WhateverHappensNextAsync();
                }
                else { /* Handle timeout */ }
            }
            finally
            {
                SyncItems.TryRemove(id, out SemaphoreSlim _);
            }
        }
        else { /* Handle very unexpected */ }
    }

    return Ok();
}

[HttpGet("api2/{id}")]
public IActionResult Api2(Guid id)
{
    if (SyncItems.TryRemove(id, out SemaphoreSlim semaphore))
    {
        semaphore.Release();
        return Ok();
    }

    return NotFound();
}

Ben Voigt关于使用TaskCompletionSource的建议可能更纯净。只需将其替换为SemaphoreSlim,再加上一些小的更改即可。 SemaphoreSlim非常轻巧,使用起来也更容易一些。使用TCS时,您需要使用定时CancellationTokenSource来实现超时,该定时.Register()要在TCS上调用.TrySetCanceled()