如何为Blazor页面实现mutex互斥锁 同一时间仅允许单用户操作相关功能
Blazor 页面功能全局互斥执行实现方案
首先明确:不管是Blazor Server还是Blazor WebAssembly架构,该互斥逻辑必须放在后端服务层统一管控,不能在前端页面实例内实现,否则多用户/多页面实例场景下锁会完全失效。
实现步骤
第一步:定义全局互斥锁单例服务
推荐使用SemaphoreSlim实现异步锁,适配ASP.NET Core全异步的请求处理模型,比原生Mutex更适配Web服务场景。服务代码如下:public interface IGlobalOperationLockService { /// <summary> /// 等待获取锁 /// </summary> /// <param name="timeout">最长等待时间</param> /// <returns>是否成功获取到锁</returns> Task<bool> WaitForLockAsync(TimeSpan timeout); /// <summary> /// 释放锁 /// </summary> void ReleaseLock(); } public class GlobalOperationLockService : IGlobalOperationLockService, IDisposable { // 初始化信号量允许并发数为1,实现同一时间仅一个请求持有锁 private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private bool _isDisposed; public async Task<bool> WaitForLockAsync(TimeSpan timeout) { return await _semaphore.WaitAsync(timeout); } public void ReleaseLock() { // 避免未持有锁时调用释放抛出异常 if (_semaphore.CurrentCount == 0) { _semaphore.Release(); } } public void Dispose() { if (_isDisposed) return; _semaphore.Dispose(); _isDisposed = true; } }在
Program.cs中将该服务注册为全局单例:builder.Services.AddSingleton<IGlobalOperationLockService, GlobalOperationLockService>();第二步:在业务逻辑中接入锁
必须搭配try-finally结构使用,确保无论业务逻辑执行成功还是抛出异常,锁都能被正常释放,避免出现死锁。页面侧使用示例如下:// 注入全局锁服务 [Inject] private IGlobalOperationLockService LockService { get; set; } /// <summary> /// 你要加锁的数据库操作触发方法 /// </summary> private async Task ExecuteDatabaseOperation() { // 自定义最长等待时间,示例设置为30秒,避免用户无限等待 var getLockSuccess = await LockService.WaitForLockAsync(TimeSpan.FromSeconds(30)); if (!getLockSuccess) { // 此处可添加获取锁超时的用户提示,比如"当前操作繁忙,请稍后重试" return; } try { // 这里写入你原本的数据库操作业务逻辑 await YourDatabaseBusinessLogic(); } finally { // 强制释放锁 LockService.ReleaseLock(); } }
注意事项
- 上述实现为单服务实例场景下的内存锁,如果你采用后端集群、多实例部署的架构,本地内存锁会失效,需要替换为分布式锁实现(可基于Redis、数据库公共行锁等实现,使用逻辑和上述流程一致,仅锁的存储载体从本地内存更换为多实例共享的公共存储)。
- 锁的超时时间需要根据你的业务逻辑平均执行时长合理设置,避免设置过短导致业务未执行完就提前释放锁,也避免设置过长导致后续用户等待时间太久。
- 建议搭配数据库层面的事务、乐观锁/悲观锁共同使用,双重保障数据操作的一致性。
内容的提问来源于stack exchange,提问作者Tessaract
相关产品推荐
相关产品推荐

