You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

分布式环境中批量获取对象锁后执行操作,try-finally是否安全?

Question

I have a collection of objects: List<SubscriptionSeat> seatsToRemove; each SubscriptionSeat object has a GetModifyLock() method, which is used to lock the object before modification in a distributed environment. The usage is as follows:

var seatToRemove = seatsToRemove[0];
using(seatToRemove.GetModifyLock()) {
    seatToRemove.HardDelete();
}

I need to perform operations on all objects in seatsToRemove, but I want to acquire all the locks first before executing the target operation. Is using a try-finally block the safest implementation? This approach needs to ensure that all locks are properly released, and operations are only executed after all locks are acquired. My implementation code is as follows:

var objLocks = new List<DistributedLock>();
try {
    // 获取锁
    foreach (var seatToRemove in seatsToRemove) {
        objLocks.Add(seatToRemove.GetModifyLock());
    }
    // 执行加锁后的操作
    foreach (var seatToRemove in seatsToRemove) {
        seatToRemove.HardDelete();
    }
} finally {
    // 清理资源
    foreach (var objLock in objLocks) {
        objLock.Dispose();
    }
}

Answer

Great question! Let's break this down to assess your approach and explore how to make it even more robust.

First off: Yes, using a try-finally block is a safe, reliable foundation for this scenario. The key strength here is that finally blocks run unconditionally—whether your try block completes successfully, throws an exception, or gets interrupted—so you're guaranteed to release every lock you've already acquired. In distributed systems, this is critical to avoid orphaned locks that could cause deadlocks or resource leaks down the line.

Let's verify how your code handles critical edge cases:

  • Full lock acquisition success: All locks get added to objLocks, your HardDelete operations run without issue, and finally cleans up every lock perfectly.
  • Partial lock acquisition failure: If, say, the 3rd seat's GetModifyLock() times out and throws an exception, the try block exits immediately. The finally block then disposes all locks that were already added (the first 2 in this example), preventing partial lock leaks. This is exactly the behavior you want.

That said, here are a few small tweaks to make your code more resilient and maintainable:

  1. Use List<IDisposable> for flexibility: Since DistributedLock implements IDisposable, storing locks in a List<IDisposable> makes your code less tied to the specific lock type, which helps with future changes.
  2. Add null checks for GetModifyLock(): If there's any chance the method could return null (e.g., a seat was deleted before you tried to lock it), skip adding nulls to the list to avoid NullReferenceException during disposal.
  3. Sort seats before locking: In distributed environments, acquiring locks in a consistent, deterministic order (like sorting seats by their unique ID) reduces the risk of deadlocks if other processes are trying to lock the same set of seats in reverse order.
  4. Validate full lock acquisition: Add a check to ensure you've acquired all expected locks before running HardDelete—this prevents partial deletions if some locks couldn't be obtained.

Here's an updated version of your code with these improvements:

var objLocks = new List<IDisposable>();
// Sort seats to avoid distributed deadlocks
var sortedSeats = seatsToRemove.OrderBy(s => s.Id).ToList();

try {
    // Acquire locks, skipping nulls if needed
    foreach (var seat in sortedSeats) {
        var distributedLock = seat.GetModifyLock();
        if (distributedLock != null) {
            objLocks.Add(distributedLock);
        }
        // If GetModifyLock() throws on failure, finally will clean up existing locks automatically
    }

    // Ensure we have all locks before proceeding
    if (objLocks.Count != sortedSeats.Count) {
        throw new InvalidOperationException("Failed to acquire all required locks for deletion.");
    }

    // Execute the protected operations
    foreach (var seat in sortedSeats) {
        seat.HardDelete();
    }
} finally {
    // Dispose every acquired lock, no matter what happens
    foreach (var lockObj in objLocks) {
        lockObj.Dispose();
    }
}

One final check: Make sure your DistributedLock's Dispose() method properly releases the lock in your distributed store (e.g., releasing a Redis lock, updating a database lock flag). As long as that implementation is solid, your try-finally approach will keep your lock handling safe and predictable.

内容的提问来源于stack exchange,提问作者Tom Gullen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:24:58