English 中文(简体)
如何在ASP.NET中正确地注入SemaphoreSlim
原标题:How to properly inject a SemaphoreSlim in ASP.NET

我想了解如何基于头值应用SemaphoreSlim(或amy其他用于并发的locker)。

比方说,我有一个多个客户端使用的应用程序,我想将一些方法限制为每个客户端只能调用一个。

我的主代码如下所示:

private readonly SemaphoreSlim semaphore;

public class(SemaphoreSlim semaphore)
{
  this.semaphore = semaphore;
}

public async Task SomeMethodAsync()
{
  await this semaphore.WaitAsync()
  
  try
  {
    // await some async stuff
  }
  finally
  {
    semaphore.Release();
  }
}

我试图在Program.cs上注入SemaphoreSlim,比如

private static readonly ConcurrentDictionary<string, SemaphoreSlim> semaphores = new ();

internal static IServiceCollection ConfigureSemaphore(this IServiceCollection services)
{
    return services.AddScoped(provider =>
    {
        IHttpContextAccessor context = provider.GetRequiredService<IHttpContextAccessor>();
        string? clientId = (context.HttpContext?.Request.Headers["clientId"])?.ToString();
        return semaphores.GetOrAdd(clientId ?? "0", _ => new SemaphoreSlim(1));
    });
}

这样,我就知道每个客户端都会有自己的基于clientId头的信号量。

对我来说很有道理,但我确信我遗漏了一些东西,因为当我启动API时,第一个调用有效,但下一个具有相同clientId头的调用抛出异常,表明SemaphoreSlim已被释放。

我知道需要对注入进行Scoped,因为我需要检查每个请求的头值,但当将其切换到Singleton时,Semaphore实例不会在第一次调用结束时被处理。这不会起作用,因为Singleton注入会为任何clientId注入相同的Semaphore,这不是我们的想法。

So... What should I do to avoid the SemaphoreSlim being disposed? Or... What would be the correct approach to limit the concurrency by client?

(我检查了lock关键字,但它不适用于异步调用)

问题回答

正如您所看到的,当请求结束时,DI容器正在处理您的SemaphoreSlim,并且您最终得到了一个缓存的已处理实例。我会改变设计,不注入具体的SemaphoreSlim,而是注入一些提供者类型的接口。

public interface ISemaphoreSlimManager
{
    SemaphoreSlim Get(string clientId);
}

public class SemaphoreSlimManager : ISemaphoreSlimManager
{
    private static readonly ConcurrentDictionary<string, SemaphoreSlim> Semaphores = new();
    public SemaphoreSlim Get(string clientId)
    {
        return Semaphores.GetOrAdd(clientId ?? "0", _ => new SemaphoreSlim(1));
    }
}

将其注册为单例

services.AddSingleton<ISemaphoreSlimManager, SemaphoreSlimManager>();

然后在控制器中执行以下操作:

var semaphore = _semaphoreSlimManager.Get(HttpContext?.Request.Headers["clientId"])?.ToString();

我之所以不将IHttpContextAccessor注入SemaphoreSlimManager,是因为这是一个通用的实用程序类,理论上应该可以工作,而不必在ASP Core应用程序的上下文中。





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

How to Add script codes before the </body> tag ASP.NET

Heres the problem, In Masterpage, the google analytics code were pasted before the end of body tag. In ASPX page, I need to generate a script (google addItem tracker) using codebehind ClientScript ...

Transaction handling with TransactionScope

I am implementing Transaction using TransactionScope with the help this MSDN article http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx I just want to confirm that is ...

System.Web.Mvc.Controller Initialize

i have the following base controller... public class BaseController : Controller { protected override void Initialize(System.Web.Routing.RequestContext requestContext) { if (...

Microsoft.Contracts namespace

For what it is necessary Microsoft.Contracts namespace in asp.net? I mean, in what cases I could write using Microsoft.Contracts;?

Separator line in ASP.NET

I d like to add a simple separator line in an aspx web form. Does anyone know how? It sounds easy enough, but still I can t manage to find how to do it.. 10x!

热门标签