【问题标题】:Is this how to write a Thread-Safe IComparable?这是如何编写线程安全的 IComparable 吗?
【发布时间】:2026-01-07 15:00:02
【问题描述】:

我有一个简单的类,我想让它成为线程安全的。该类需要实现 IComparer。我知道以线程安全的方式实现int CompareTo(T other) 并不简单。如果我没有以正确的方式锁定,很容易出现死锁。我有三个问题:

  1. 此代码是线程安全的吗?如果没有,我该如何解决?
  2. 这个代码可以再短一点吗?一个简单的减法似乎有很多代码。
  3. 我是否应该费心让int CompareTo(T other) 线程安全?我是否应该要求调用者(通常是排序)锁定所有相关的 BObject?

这是我的代码:

public class BObject : IComparable<BObject>
{
    //Each BObject has a unique object id
    private static int _bObjectId = 0;
    private static int GetNextId()
    {
        return System.Threading.Interlocked.Increment(ref BObject._bObjectId);
    }

    private object _locker = new object();
    private readonly int _id = BObject.GetNextId();

    //Some variable
    private int _sales;
    public int Sales
    {
        get
        {
            lock (this._locker)
                return this._sales;
        }
        set
        {
            lock (this._locker)
                this._sales = value;
        }
    }

    public int CompareTo(BObject other)
    {
        int result;

        //Can I simply do "if (this._id == other._id)"
        if (object.ReferenceEquals(this, other))
            result = 0;
        else
        {
            //get the BObject with the lower id
            BObject lower = this._id < other._id ? this : other;

            //get the BObject with the higher id
            BObject higher = this._id > other._id ? this : other;

            //lock the BObject with the lower id first
            lock (lower._locker)
            {
                //lock the BObject with the higher id last
                lock (higher._locker)
                {
                    //put object with lower Sales first
                    result = this.Sales - other.Sales;
                }
            }
        }

        return result;
    }
}

【问题讨论】:

  • 锁定每个成员并不能使您的代码线程安全。
  • 使比较线程安全的最简单方法:使您的类型不可变。
  • 将此评论:lock the BObject with the higher id first 更改为 ...last
  • 简而言之,线程安全是hard。您需要弄清楚哪些操作可以同时发生,以及无论发生什么,如何确保您处于一致的状态。不可变对象使这非常更容易做到。
  • @user2023861 字符串是不可变的,所以它们没有问题。您只需放置两个字段。例如 int Sales 和 DateTime LastSale

标签: c# thread-safety locking icomparer


【解决方案1】:

您希望这种比较在什么使用条件下与被比较的值发生突变同时发生?在这些条件下,什么行为应该是“正确的”?一旦定义了正确性的标准,您就可以设计一种方法来实现线程安全。

线程安全实际上是关于如何使用事物以及这种使用如何跨线程边界进行交互。因此,例如,如果您正在对这些对象的列表进行排序,然后同时对集合进行变异,您可能需要某种方法来防止在排序过程中发生变异。最坏的情况,您可能会想出一个场景,您正在以一种导致排序永远不会终止的方式改变实例(这将非常棘手,但理论上是可能的。)简而言之,您需要更多地考虑您如何使用这些实例的高级观点。最有可能的是,这不是可以在实例访问器级别上实现“线程安全”的东西。

【讨论】:

  • you might want some way to prevent mutations while in the middle of sorting 这是我对第三个问题的想法。在这种情况下,让我的 CompareTo 方法线程安全是没有意义的。