我将此类实现为单例。我不擅长线程安全。想要确保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++);
}
}
答案 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;
}
}