1

我有这种情况下,我有一个WebApi和一个端点,当被触发时做了很多工作(大约2-5分钟)。这是一个带有副作用的POST端点,我想限制执行,以便如果发送2个请求到这个端点(不应该发生,但是比对不起更安全),其中一个请等待以避免竞争条件。如何在WebApi操作中锁定长时间的异步调用?

我第一次尝试使用一个简单的静态锁控制器内是这样的:

lock (_lockObj) 
{ 
    var results = await _service.LongRunningWithSideEffects(); 
    return Ok(results); 
} 

这是因为lock语句内await的当然是不可能的。

我考虑的另一个解决方案是使用一个SemaphoreSlim实现这样的:

await semaphore.WaitAsync(); 
try 
{ 
    var results = await _service.LongRunningWithSideEffects(); 
    return Ok(results); 
} 
finally 
{ 
    semaphore.Release(); 
} 

然而,根据MSDN:

的SemaphoreSlim类表示可用于重量轻,快速旗语等待时间在之间,等待时间预计会很短

由于在这种情况下,等待时间甚至可能会达到5分钟,我应该如何使用并发控制?

EDIT(响应plog17):

我明白,通过此任务到服务可能是最佳的方式,但是,我不一定要排队在仍然运行在后台的东西请求完成后。 该请求涉及需要一些时间的其他请求和集成,但我仍然希望用户等待此请求完成并获得响应。 这个请求预计只能在特定时间每天由cron作业触发一次。但是,也有一个选择可以由开发人员手动触发它(主要是为了防止作业出现问题),并且我想确保如果开发人员(例如,开发人员)不会遇到并发问题偶然发送请求等。

回答

1

如果在给定的时间只有一个请求可以被处理,那么为什么不实施一个队列呢?

有了这样的设计,在处理长时间运行的请求时不再需要锁定或等待。

流量可能是:

  1. 客户端POST/RessourcesToProcess,应该得到迅速202接受的
  2. HttpController简单地排队进行任务(并返回202接受的)

  3. 其他服务(Windows服务?)出队接下来的任务进行

  4. 继续任务
  5. 更新资源状态

在这个过程中,客户端应该能够轻松获得以前的请求的状态:

  • 如果任务不发现:404-NotFound。找不到id的资源123
  • 如果任务处理:200-OK。 123正在处理中。
  • 如果完成任务:200-确定。处理响应。

你的控制器可能看起来像:

public class TaskController 
{ 

    //constructor and private members 

    [HttpPost, Route("")] 
    public void QueueTask(RequestBody body) 
    { 
     messageQueue.Add(body); 
    } 

    [HttpGet, Route("taskId")] 
    public void QueueTask(string taskId) 
    { 
     YourThing thing = tasksRepository.Get(taskId); 

     if (thing == null) 
     { 
      return NotFound("thing does not exist"); 
     } 
     if (thing.IsProcessing) 
     { 
      return Ok("thing is processing"); 
     } 
     if (!thing.IsProcessing) 
     { 
      return Ok("thing is not processing yet"); 
     } 
     //here we assume thing had been processed 
     return Ok(thing.ResponseContent); 
    } 
} 

这种设计意味着你不处理你的WebAPI内长时间运行的过程。事实上,它可能不是最好的设计选择。如果您仍然想这样做,你可能需要阅读:

+0

因为评论字数不多的,我在编辑的形式回答,请检查更新的问题。 – valorl