SemaphoreSlim 不对并发请求进行排队,仍然允许重复预订
SemaphoreSlim doesn't queue concurrent requests and still allows double bookings
我在 .NET Core (2.2) 中使用 SemaphoreSlim class 时遇到问题,希望有人能提供帮助。
我有一个 API 方法 (AddBooking),可以将预订添加到数据库中,此方法包括 3 个等待的存储库 AddAsync() 方法,可将数据添加到 3 个单独的表中。
我看到的所有 guides/docs class 用法略有不同,但 none 似乎实际上是在排队并发请求,导致重复预订。
单步执行代码时,信号量的 CurrentValue 在请求进行时从 1 变为 0,然后在调用 Release() 后变回 1。
private readonly SemaphoreSlim _asyncLock;
_asyncLock = new SemaphoreSlim(1); // in the constructor
public async Task<BookingResponse> AddBooking(NewBooking newBooking)
{
await _asyncLock.WaitAsync().ConfigureAwait(false);
try
{
if (!IsWithinBookingTimeframe(newBooking))
return new BookingResponse { Result = false, Message = "Bookings must be between 6am and 8pm" };
AmendIfDayLightSavings(newBooking);
var bookingDetailsResponse = await CreateBookingDetails(newBooking);
if (!bookingDetailsResponse.Result)
return bookingDetailsResponse;
var parkingSpaceBookingResponse = await CreateParkingSpaceBooking(newBooking, bookingDetailsResponse.BookingDetails);
if (!parkingSpaceBookingResponse.Result)
return parkingSpaceBookingResponse;
await _unitOfWork.Save();
return new BookingResponse { Task = "AddBooking", Result = true, Message = "Booking added successfully", BookingDetails = bookingDetailsResponse.BookingDetails, ParkingSpaces = parkingSpaceBookingResponse.ParkingSpaces };
}
catch (Exception exception)
{
return new BookingResponse { Task = "AddBooking", Result = false, Message = $"Error adding booking to database: {exception}" };
}
finally
{
_asyncLock.Release();
}
}
我对这个class的理解是,如果CurrentCount为0,后续请求会排队,直到信号量被释放,CurrentCount递增到1(从而允许下一个任务通过),但是同时发出请求仍然会导致重复预订。
一切都取决于在何处以及由谁实例化您的异步时钟。您可能有多个 class 实例,每个实例都有自己的锁,这使得它们看不到其他请求。
基本上你会收到一个请求 -> 实例化你的 class -> 获取锁 -> 生成预订
您必须确保信号量实例在您的 class 实例之间共享,这将 运行 此代码。
作为提示,将 log/breakpoint 插入到 class 构造函数中。您可能会看到它被多次实例化(并创建多个信号量)
你应该这样称呼它:_asyncLock = new SemaphoreSlim(1, 1);
因为第一个参数是初始计数,第二个是最大计数。
现在你只提供了一个初始计数,在第一次调用 Release()
之后你可以无限制地调用它。
或者换句话说,第二个参数禁止超过n个并发调用。
第一个参数表示在第一次调用 Release()
之前可以进行多少次调用。
我在 .NET Core (2.2) 中使用 SemaphoreSlim class 时遇到问题,希望有人能提供帮助。
我有一个 API 方法 (AddBooking),可以将预订添加到数据库中,此方法包括 3 个等待的存储库 AddAsync() 方法,可将数据添加到 3 个单独的表中。
我看到的所有 guides/docs class 用法略有不同,但 none 似乎实际上是在排队并发请求,导致重复预订。
单步执行代码时,信号量的 CurrentValue 在请求进行时从 1 变为 0,然后在调用 Release() 后变回 1。
private readonly SemaphoreSlim _asyncLock;
_asyncLock = new SemaphoreSlim(1); // in the constructor
public async Task<BookingResponse> AddBooking(NewBooking newBooking)
{
await _asyncLock.WaitAsync().ConfigureAwait(false);
try
{
if (!IsWithinBookingTimeframe(newBooking))
return new BookingResponse { Result = false, Message = "Bookings must be between 6am and 8pm" };
AmendIfDayLightSavings(newBooking);
var bookingDetailsResponse = await CreateBookingDetails(newBooking);
if (!bookingDetailsResponse.Result)
return bookingDetailsResponse;
var parkingSpaceBookingResponse = await CreateParkingSpaceBooking(newBooking, bookingDetailsResponse.BookingDetails);
if (!parkingSpaceBookingResponse.Result)
return parkingSpaceBookingResponse;
await _unitOfWork.Save();
return new BookingResponse { Task = "AddBooking", Result = true, Message = "Booking added successfully", BookingDetails = bookingDetailsResponse.BookingDetails, ParkingSpaces = parkingSpaceBookingResponse.ParkingSpaces };
}
catch (Exception exception)
{
return new BookingResponse { Task = "AddBooking", Result = false, Message = $"Error adding booking to database: {exception}" };
}
finally
{
_asyncLock.Release();
}
}
我对这个class的理解是,如果CurrentCount为0,后续请求会排队,直到信号量被释放,CurrentCount递增到1(从而允许下一个任务通过),但是同时发出请求仍然会导致重复预订。
一切都取决于在何处以及由谁实例化您的异步时钟。您可能有多个 class 实例,每个实例都有自己的锁,这使得它们看不到其他请求。
基本上你会收到一个请求 -> 实例化你的 class -> 获取锁 -> 生成预订
您必须确保信号量实例在您的 class 实例之间共享,这将 运行 此代码。
作为提示,将 log/breakpoint 插入到 class 构造函数中。您可能会看到它被多次实例化(并创建多个信号量)
你应该这样称呼它:_asyncLock = new SemaphoreSlim(1, 1);
因为第一个参数是初始计数,第二个是最大计数。
现在你只提供了一个初始计数,在第一次调用 Release()
之后你可以无限制地调用它。
或者换句话说,第二个参数禁止超过n个并发调用。
第一个参数表示在第一次调用 Release()
之前可以进行多少次调用。