我有一个.NET服务,该服务具有2个API调用。这是我的情况:
我想获得一些帮助或建议,以使API 1异步等待来自API 2的信号(在另一个线程上)是正确的技术选择。
(理想情况下仅使用.net库,而不使用外部库。)
答案 0 :(得分:0)
我认为方程式的缺失部分是状态和唯一标识符。 API 1将带有标识符的响应推送到客户端后,而不是等待,您应该开始监视状态存储,以有意义的时间间隔进行轮询(我不知道延迟时间,所以可能是一秒钟,一分钟,或更多),以查找您刚刚发送的标识符。
客户端接收推送并使用标识符调用API 2。 API 2接收带有唯一令牌的调用,该令牌只有API 1才能生成,并且专用于此系列调用。 API 2完成工作后,会将唯一标识符和结果推送到状态存储中。 API 2退出,该线程被杀死。
与此同时,API 1一直在使用其唯一的标识符轮询状态存储,现在可以看到数据可用。退出等待状态,然后开始处理新发现的数据。
可以在内存中,基于文件的数据库中完成,也可以与云服务结合使用。但是总体架构不是直接控制CPU线程,而是沿着服务总线进行通信,并允许各方发出/订阅各种事件并确定何时响应/做出反应。
答案 1 :(得分:0)
您可以使用ConcurrentDictionary
和SemaphoreSlim
:
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()
。