这个类线程安全吗?

时间:2010-12-07 00:06:37

标签: c# multithreading

我将此类实现为单例。我不擅长线程安全。想要确保GenerateOrderID类是线程安全的。更具体地说,orderCount变量不能由不同的对象同时递增并抛弃计数。

public class OrderIDGenerator

{

    private static readonly OrderIDGenerator instance = new OrderIDGenerator();
    private int orderCount;


    private OrderIDGenerator()
    {
        orderCount = 1;
    }


    public static OrderIDGenerator Instance
    {
        get { return instance; }
    }

    public string GenerateOrderID()
    {
        return String.Format("{0:yyyyMMddHHmmss}{1}", DateTime.Now, orderCount++);
    }

}

4 个答案:

答案 0 :(得分:8)

不是。后增量操作不是原子操作。您需要进行以下更改:

GenerateOrderID方法替换为:

public string GenerateOrderID()
{
    return String.Format("{0:yyyyMMddHHmmss}{1}",
                         DateTime.Now,
                         Interlocked.Increment(ref orderCount));
}

并将orderCount初始化为0而不是1,因为Interlocked.Increment返回递增的值。 (换句话说,Interlocked.Increment(ref foo)在各方面都与++foo相同,只不过它是原子的,因此是线程安全的。)

请注意,Interlocked.Increment比使用lock同步线程更有效,但lock仍可正常工作。请参阅this question

另外,不要使用单身人士。

答案 1 :(得分:3)

这个类不是线程安全的。

增量不是原子操作。因此,完全有可能在错误的时间发生上下文切换:

Start with orderCount at 1.
Two threads try to GenerateOrderID() at once:

Thread 1             |    Thread 2
----------------------------------------------
read orderCount = 1  |
                    -->
                     |  read orderCount = 1
                     |  add: 1 + 1
                     |  write orderCount = 2
                    <--
add: 1 + 1           |
write orderCount = 2 |
return timestamp1    |
                    -->
                     |  return timestamp1

您现在有一个重复的订单ID,并且您的订单计数已被删除。

要解决此问题,请在访问时锁定orderCount:

public string GenerateOrderID()
{
    lock(this){
        return String.Format("{0:yyyyMMddHHmmss}{1}", DateTime.Now, orderCount++);
    }
}

对于锁定的每个对象,“lock”语句一次只允许一个线程进入。如果一个线程在GenerateOrderID()中,那么在它完成之前,您的OrderIDGenerator将被锁定,并且尝试声明锁定的下一个线程将必须等到第一个线程完全完成。

详细了解lock声明here

答案 2 :(得分:1)

我会将orderCount标记为volatile(以防止编译器优化假设)并在增量周围使用锁定。挥发性可能是过度杀伤但不会受到伤害。

public class OrderIDGenerator
{

    private static readonly OrderIDGenerator instance = new OrderIDGenerator();
    private volatile int orderCount;
    private static object syncRoot = new object();


    private OrderIDGenerator()
    {
        orderCount = 1;
    }


    public static OrderIDGenerator Instance
    {
        get { return instance; }
    }

    public string GenerateOrderID()
    {
        lock (syncRoot)
            return String.Format("{0:yyyyMMddHHmmss}{1}", DateTime.Now, orderCount++);
    }
}

答案 3 :(得分:0)

您可以通过一些小修改让它成为线程安全的

public class OrderIDGenerator {
    private static readonly OrderIDGenerator instance = new OrderIDGenerator();
    private int orderCount;
    private static readonly object _locker = new object();

    private OrderIDGenerator() {
            orderCount = 1;
    }

    public static OrderIDGenerator Instance {
            get { return instance; }
    }

    public string GenerateOrderID() {
        string orderId = "";
        lock (_locker) {
            orderID = String.Format("{0:yyyyMMddHHmmss}{1}", DateTime.Now, orderCount++);
        }
        return orderID;
    }
}