如何为.NET Core 5中MediatR的瞬时服务添加线程锁?
.NET Core 5中MediatR处理器锁失效的修复方案
我在.NET Core 5中使用MediatR处理任务时,尝试添加线程锁但未生效,原因是MediatR默认的处理器并非单例服务,每个请求都会创建新的处理器实例,导致锁对象无法共享。
我的处理器代码如下:
public class MyCommandHandler : IRequestHandler<MyCommand, bool> { private object thisLock = new(); private readonly IService _service; public MyCommandHandler(IService service) { _service = service; } public async Task<bool> Handle(MyCommand request, CancellationToken cancellationToken) { var items = _service.Get().Result; lock (thisLock) { // 需同步的业务逻辑 await _service.Add(new Foo{ Id = 1 }); } } }
Startup配置:
services.AddMediatR(Assembly.GetExecutingAssembly()); services.AddSingleton<IService , MyService >();
我曾尝试将处理器注册为单例,但因写法错误出现运行时错误:
services.AddSingleton(typeof(IRequestHandler<,>), typeof(IRequestHandler<MyCommand, bool>));
以下是几种可行的修复方案:
方案1:将锁对象移至单例服务中
由于IService已注册为单例,可将锁对象内置到MyService中,确保所有处理器实例共享同一锁:
修改MyService代码:
public class MyService : IService { private readonly object _lockObj = new(); public async Task Add(Foo foo) { lock (_lockObj) { // 执行需要同步的逻辑 // 注意:若包含异步操作,lock会阻塞线程,建议改用SemaphoreSlim } } // 其他业务方法... }
如果Add方法包含异步操作,推荐使用SemaphoreSlim支持异步等待,避免线程阻塞:
public class MyService : IService { private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); public async Task Add(Foo foo) { await _semaphore.WaitAsync(); try { // 异步业务逻辑 } finally { _semaphore.Release(); } } }
方案2:正确将MediatR处理器注册为单例
之前的单例注册写法错误,需直接注册具体的处理器类型为单例:
修改Startup中的MediatR注册代码:
services.AddMediatR(cfg => { cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()); // 将指定处理器注册为单例 cfg.AddSingletonHandler<MyCommandHandler>(); }); // 或单独注册处理器: services.AddSingleton<IRequestHandler<MyCommand, bool>, MyCommandHandler>();
注册完成后,处理器会以单例形式存在,thisLock会被所有请求共享,锁即可生效。注意:若处理器内存在其他非线程安全的状态,需确保其线程安全性。
方案3:使用全局静态锁对象
在处理器类中定义静态锁对象,无论处理器是否为单例,所有实例都会共享该锁:
public class MyCommandHandler : IRequestHandler<MyCommand, bool> { private static readonly object _globalLock = new(); // 静态全局锁 private readonly IService _service; public MyCommandHandler(IService service) { _service = service; } public async Task<bool> Handle(MyCommand request, CancellationToken cancellationToken) { var items = await _service.Get(); // 建议用await替代.Result,避免死锁 lock (_globalLock) { // 需同步的业务逻辑 await _service.Add(new Foo{ Id = 1 }); } } }
注意:静态锁是全局范围的,可能会影响其他无需同步的操作,需谨慎使用。另外,_service.Get().Result易引发死锁,建议替换为异步等待的await _service.Get()。
内容的提问来源于stack exchange,提问作者AshNawa
相关产品推荐
相关产品推荐

