构建线程安全的 GUID 增量器
Building a thread-safe GUID increment'er
在我下面的代码中,我锁定了 guid,以尝试使其线程安全。
在我的示例应用程序中,大约每 10 次我 运行 程序就会得到一个 "duplicate key"。 Aka,我得到了一个副本,这不是我需要的。
有没有办法使“.NextGuid”线程安全?
using System;
namespace MyConsoleOne.BAL
{
public class GuidStore
{
private static object objectlock = new object();
private Guid StartingGuid { get; set; }
private Guid? LastGuidHolder { get; set; }
public GuidStore(Guid startingGuid)
{
this.StartingGuid = startingGuid;
}
public Guid? GetNextGuid()
{
lock (objectlock)
{
if (this.LastGuidHolder.HasValue)
{
this.LastGuidHolder = Increment(this.LastGuidHolder.Value);
}
else
{
this.LastGuidHolder = Increment(this.StartingGuid);
}
}
return this.LastGuidHolder;
}
private Guid Increment(Guid guid)
{
byte[] bytes = guid.ToByteArray();
byte[] order = { 15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3 };
for (int i = 0; i < 16; i++)
{
if (bytes[order[i]] == byte.MaxValue)
{
bytes[order[i]] = 0;
}
else
{
bytes[order[i]]++;
return new Guid(bytes);
}
}
throw new OverflowException("Guid.Increment failed.");
}
}
}
using MyConsoleOne.BAL;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyConsoleOne
{
class Program
{
static void Main(string[] args)
{
GuidStore gs = new GuidStore(Guid.NewGuid());
for (int i = 0; i < 1000; i++)
{
Console.WriteLine(i);
Dictionary<Guid, int> guids = new Dictionary<Guid, int>();
Parallel.For(0, 1000, j =>
{
Guid? currentGuid = gs.GetNextGuid();
guids.Add(currentGuid.Value, j);
Console.WriteLine(currentGuid);
}); // Parallel.For
}
Console.WriteLine("Press ENTER to Exit");
Console.ReadLine();
}
}
}
我的代码是以下组合:
- Making Guid properties threadsafe
因为我收到 "Why not use Guid.NewGuid" 个问题,我将在此处提供原因:
我有一个父进程,它有一个由 Guid.NewGuid() 创建的唯一标识符。我将其称为 "parent guid"。该父进程将创建 N 个文件。如果我从头开始编写,我会在文件名末尾附加 "N"。例如,如果父 Guid 是“11111111-1111-1111-1111-111111111111”,我会写文件
"11111111-1111-1111-1111-111111111111_1.txt"
"11111111-1111-1111-1111-111111111111_2.txt"
"11111111-1111-1111-1111-111111111111_3.txt"
,等等,等等。但是,通过现有的 "contract" 和客户端 ::: 文件名中必须有一个(唯一的)Guid,而不是 "N" (1,2等)文件名中的值(这个 "contract" 已经存在多年,所以它几乎是固定不变的)。有了这里列出的功能,我将能够保留 "contract",但文件名与 "parent" Guid 松散相关(同样,父项由 Guid.NewGuid 生成()).冲突 不是 文件名问题(它们被放在不同的文件夹下以供 'process' 执行)。碰撞是 "parent" Guid 的一个问题。但同样,这已经用 Guid.NewGuid.
处理了
因此,使用“11111111-1111-1111-1111-111111111111”的起始 Guid,我将能够编写如下文件名:
OTHERSTUFF_111111111-1111-1111-1111-111111111112_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111113_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111114_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111115_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111116_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111117_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111118_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111119_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-11111111111a_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-11111111111b_MORESTUFF.txt
所以在我上面的示例中,"parent guid" 由 "this.StartingGuid" 表示......然后我从中得到 "incremented" guid。
还有。我可以编写更好的单元测试,因为现在我可以提前知道文件名。
追加:
最终代码版本:
public class GuidStore
{
private static object objectlock = new object();
private static int[] byteOrder = { 15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3 };
private Guid StartingGuid { get; set; }
private Guid? LastGuidHolder { get; set; }
public GuidStore(Guid startingGuid)
{
this.StartingGuid = startingGuid;
}
public Guid GetNextGuid()
{
return this.GetNextGuid(0);
}
public Guid GetNextGuid(int firstGuidOffSet)
{
lock (objectlock)
{
if (this.LastGuidHolder.HasValue)
{
this.LastGuidHolder = Increment(this.LastGuidHolder.Value);
}
else
{
this.LastGuidHolder = Increment(this.StartingGuid);
for (int i = 0; i < firstGuidOffSet; i++)
{
this.LastGuidHolder = Increment(this.LastGuidHolder.Value);
}
}
return this.LastGuidHolder.Value;
}
}
private static Guid Increment(Guid guid)
{
var bytes = guid.ToByteArray();
var canIncrement = byteOrder.Any(i => ++bytes[i] != 0);
return new Guid(canIncrement ? bytes : new byte[16]);
}
}
和单元测试:
public class GuidStoreUnitTests
{
[TestMethod]
public void GetNextGuidSimpleTest()
{
Guid startingGuid = new Guid("11111111-1111-1111-1111-111111111111");
GuidStore gs = new GuidStore(startingGuid);
List<Guid> guids = new List<Guid>();
const int GuidCount = 10;
for (int i = 0; i < GuidCount; i++)
{
guids.Add(gs.GetNextGuid());
}
Assert.IsNotNull(guids);
Assert.AreEqual(GuidCount, guids.Count);
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111112")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111113")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111114")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111115")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111116")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111117")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111118")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111119")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-11111111111a")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-11111111111b")));
}
[TestMethod]
public void GetNextGuidWithOffsetSimpleTest()
{
Guid startingGuid = new Guid("11111111-1111-1111-1111-111111111111");
GuidStore gs = new GuidStore(startingGuid);
List<Guid> guids = new List<Guid>();
const int OffSet = 10;
guids.Add(gs.GetNextGuid(OffSet));
Assert.IsNotNull(guids);
Assert.AreEqual(1, guids.Count);
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-11111111111c")));
}
[TestMethod]
public void GetNextGuidMaxRolloverTest()
{
Guid startingGuid = new Guid("ffffffff-ffff-ffff-ffff-ffffffffffff");
GuidStore gs = new GuidStore(startingGuid);
List<Guid> guids = new List<Guid>();
const int OffSet = 10;
guids.Add(gs.GetNextGuid(OffSet));
Assert.IsNotNull(guids);
Assert.AreEqual(1, guids.Count);
Assert.IsNotNull(guids.FirstOrDefault(g => g == Guid.Empty));
}
[TestMethod]
public void GetNextGuidThreadSafeTest()
{
Guid startingGuid = Guid.NewGuid();
GuidStore gs = new GuidStore(startingGuid);
/* The "key" of the ConcurrentDictionary must be unique, so this will catch any duplicates */
ConcurrentDictionary<Guid, int> guids = new ConcurrentDictionary<Guid, int>();
Parallel.For(
0,
1000,
j =>
{
Guid currentGuid = gs.GetNextGuid();
if (!guids.TryAdd(currentGuid, j))
{
throw new ArgumentOutOfRangeException("GuidStore.GetNextGuid ThreadSafe Test Failed");
}
}); // Parallel.For
}
[TestMethod]
public void GetNextGuidTwoRunsProduceSameResultsTest()
{
Guid startingGuid = Guid.NewGuid();
GuidStore gsOne = new GuidStore(startingGuid);
/* The "key" of the ConcurrentDictionary must be unique, so this will catch any duplicates */
ConcurrentDictionary<Guid, int> setOneGuids = new ConcurrentDictionary<Guid, int>();
Parallel.For(
0,
1000,
j =>
{
Guid currentGuid = gsOne.GetNextGuid();
if (!setOneGuids.TryAdd(currentGuid, j))
{
throw new ArgumentOutOfRangeException("GuidStore.GetNextGuid ThreadSafe Test Failed");
}
}); // Parallel.For
gsOne = null;
GuidStore gsTwo = new GuidStore(startingGuid);
/* The "key" of the ConcurrentDictionary must be unique, so this will catch any duplicates */
ConcurrentDictionary<Guid, int> setTwoGuids = new ConcurrentDictionary<Guid, int>();
Parallel.For(
0,
1000,
j =>
{
Guid currentGuid = gsTwo.GetNextGuid();
if (!setTwoGuids.TryAdd(currentGuid, j))
{
throw new ArgumentOutOfRangeException("GuidStore.GetNextGuid ThreadSafe Test Failed");
}
}); // Parallel.For
bool equal = setOneGuids.Select(g => g.Key).OrderBy(i => i).SequenceEqual(
setTwoGuids.Select(g => g.Key).OrderBy(i => i), new GuidComparer<Guid>());
Assert.IsTrue(equal);
}
}
internal class GuidComparer<Guid> : IEqualityComparer<Guid>
{
public bool Equals(Guid x, Guid y)
{
return x.Equals(y);
}
public int GetHashCode(Guid obj)
{
return 0;
}
}
你这里有两个问题:
Dictionary.Add()
不是线程安全的。请改用 ConcurrentDictionary.TryAdd()
。
- 您的
GetNextGuid()
实现存在竞争条件,因为您 returning this.LastGuidHolder
处于锁外,因此它可能会在 [=38] 之前被另一个线程修改=]ed.
一个明显的解决方案是将 return 移到锁内:
public Guid? GetNextGuid()
{
lock (objectlock)
{
if (this.LastGuidHolder.HasValue)
{
this.LastGuidHolder = Increment(this.LastGuidHolder.Value);
}
else
{
this.LastGuidHolder = Increment(this.StartingGuid);
}
return this.LastGuidHolder;
}
}
但是,我会将 return 类型更改为 Guid
- 它似乎对 return 和 Guid?
没有任何作用 - 这应该是隐藏在 class:
中
public Guid GetNextGuid()
{
lock (objectlock)
{
if (this.LastGuidHolder.HasValue)
{
this.LastGuidHolder = Increment(this.LastGuidHolder.Value);
}
else
{
this.LastGuidHolder = Increment(this.StartingGuid);
}
return this.LastGuidHolder.Value;
}
}
这是您使用 ConcurrentDictionary
的测试方法的一个版本:
static void Main(string[] args)
{
GuidStore gs = new GuidStore(Guid.NewGuid());
for (int i = 0; i < 1000; i++)
{
Console.WriteLine(i);
ConcurrentDictionary<Guid, int> guids = new ConcurrentDictionary<Guid, int>();
Parallel.For(0, 1000, j =>
{
Guid currentGuid = gs.GetNextGuid();
if (!guids.TryAdd(currentGuid, j))
{
Console.WriteLine("Duplicate found!");
}
}); // Parallel.For
}
Console.WriteLine("Press ENTER to Exit");
Console.ReadLine();
}
说了这么多,我不明白你为什么不只是使用 Guid.NewGuid()
...
在我下面的代码中,我锁定了 guid,以尝试使其线程安全。 在我的示例应用程序中,大约每 10 次我 运行 程序就会得到一个 "duplicate key"。 Aka,我得到了一个副本,这不是我需要的。
有没有办法使“.NextGuid”线程安全?
using System;
namespace MyConsoleOne.BAL
{
public class GuidStore
{
private static object objectlock = new object();
private Guid StartingGuid { get; set; }
private Guid? LastGuidHolder { get; set; }
public GuidStore(Guid startingGuid)
{
this.StartingGuid = startingGuid;
}
public Guid? GetNextGuid()
{
lock (objectlock)
{
if (this.LastGuidHolder.HasValue)
{
this.LastGuidHolder = Increment(this.LastGuidHolder.Value);
}
else
{
this.LastGuidHolder = Increment(this.StartingGuid);
}
}
return this.LastGuidHolder;
}
private Guid Increment(Guid guid)
{
byte[] bytes = guid.ToByteArray();
byte[] order = { 15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3 };
for (int i = 0; i < 16; i++)
{
if (bytes[order[i]] == byte.MaxValue)
{
bytes[order[i]] = 0;
}
else
{
bytes[order[i]]++;
return new Guid(bytes);
}
}
throw new OverflowException("Guid.Increment failed.");
}
}
}
using MyConsoleOne.BAL;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyConsoleOne
{
class Program
{
static void Main(string[] args)
{
GuidStore gs = new GuidStore(Guid.NewGuid());
for (int i = 0; i < 1000; i++)
{
Console.WriteLine(i);
Dictionary<Guid, int> guids = new Dictionary<Guid, int>();
Parallel.For(0, 1000, j =>
{
Guid? currentGuid = gs.GetNextGuid();
guids.Add(currentGuid.Value, j);
Console.WriteLine(currentGuid);
}); // Parallel.For
}
Console.WriteLine("Press ENTER to Exit");
Console.ReadLine();
}
}
}
我的代码是以下组合:
- Making Guid properties threadsafe
因为我收到 "Why not use Guid.NewGuid" 个问题,我将在此处提供原因:
我有一个父进程,它有一个由 Guid.NewGuid() 创建的唯一标识符。我将其称为 "parent guid"。该父进程将创建 N 个文件。如果我从头开始编写,我会在文件名末尾附加 "N"。例如,如果父 Guid 是“11111111-1111-1111-1111-111111111111”,我会写文件
"11111111-1111-1111-1111-111111111111_1.txt"
"11111111-1111-1111-1111-111111111111_2.txt"
"11111111-1111-1111-1111-111111111111_3.txt"
,等等,等等。但是,通过现有的 "contract" 和客户端 ::: 文件名中必须有一个(唯一的)Guid,而不是 "N" (1,2等)文件名中的值(这个 "contract" 已经存在多年,所以它几乎是固定不变的)。有了这里列出的功能,我将能够保留 "contract",但文件名与 "parent" Guid 松散相关(同样,父项由 Guid.NewGuid 生成()).冲突 不是 文件名问题(它们被放在不同的文件夹下以供 'process' 执行)。碰撞是 "parent" Guid 的一个问题。但同样,这已经用 Guid.NewGuid.
处理了因此,使用“11111111-1111-1111-1111-111111111111”的起始 Guid,我将能够编写如下文件名:
OTHERSTUFF_111111111-1111-1111-1111-111111111112_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111113_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111114_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111115_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111116_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111117_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111118_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-111111111119_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-11111111111a_MORESTUFF.txt
OTHERSTUFF_111111111-1111-1111-1111-11111111111b_MORESTUFF.txt
所以在我上面的示例中,"parent guid" 由 "this.StartingGuid" 表示......然后我从中得到 "incremented" guid。
还有。我可以编写更好的单元测试,因为现在我可以提前知道文件名。
追加:
最终代码版本:
public class GuidStore
{
private static object objectlock = new object();
private static int[] byteOrder = { 15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3 };
private Guid StartingGuid { get; set; }
private Guid? LastGuidHolder { get; set; }
public GuidStore(Guid startingGuid)
{
this.StartingGuid = startingGuid;
}
public Guid GetNextGuid()
{
return this.GetNextGuid(0);
}
public Guid GetNextGuid(int firstGuidOffSet)
{
lock (objectlock)
{
if (this.LastGuidHolder.HasValue)
{
this.LastGuidHolder = Increment(this.LastGuidHolder.Value);
}
else
{
this.LastGuidHolder = Increment(this.StartingGuid);
for (int i = 0; i < firstGuidOffSet; i++)
{
this.LastGuidHolder = Increment(this.LastGuidHolder.Value);
}
}
return this.LastGuidHolder.Value;
}
}
private static Guid Increment(Guid guid)
{
var bytes = guid.ToByteArray();
var canIncrement = byteOrder.Any(i => ++bytes[i] != 0);
return new Guid(canIncrement ? bytes : new byte[16]);
}
}
和单元测试:
public class GuidStoreUnitTests
{
[TestMethod]
public void GetNextGuidSimpleTest()
{
Guid startingGuid = new Guid("11111111-1111-1111-1111-111111111111");
GuidStore gs = new GuidStore(startingGuid);
List<Guid> guids = new List<Guid>();
const int GuidCount = 10;
for (int i = 0; i < GuidCount; i++)
{
guids.Add(gs.GetNextGuid());
}
Assert.IsNotNull(guids);
Assert.AreEqual(GuidCount, guids.Count);
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111112")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111113")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111114")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111115")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111116")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111117")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111118")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-111111111119")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-11111111111a")));
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-11111111111b")));
}
[TestMethod]
public void GetNextGuidWithOffsetSimpleTest()
{
Guid startingGuid = new Guid("11111111-1111-1111-1111-111111111111");
GuidStore gs = new GuidStore(startingGuid);
List<Guid> guids = new List<Guid>();
const int OffSet = 10;
guids.Add(gs.GetNextGuid(OffSet));
Assert.IsNotNull(guids);
Assert.AreEqual(1, guids.Count);
Assert.IsNotNull(guids.FirstOrDefault(g => g == new Guid("11111111-1111-1111-1111-11111111111c")));
}
[TestMethod]
public void GetNextGuidMaxRolloverTest()
{
Guid startingGuid = new Guid("ffffffff-ffff-ffff-ffff-ffffffffffff");
GuidStore gs = new GuidStore(startingGuid);
List<Guid> guids = new List<Guid>();
const int OffSet = 10;
guids.Add(gs.GetNextGuid(OffSet));
Assert.IsNotNull(guids);
Assert.AreEqual(1, guids.Count);
Assert.IsNotNull(guids.FirstOrDefault(g => g == Guid.Empty));
}
[TestMethod]
public void GetNextGuidThreadSafeTest()
{
Guid startingGuid = Guid.NewGuid();
GuidStore gs = new GuidStore(startingGuid);
/* The "key" of the ConcurrentDictionary must be unique, so this will catch any duplicates */
ConcurrentDictionary<Guid, int> guids = new ConcurrentDictionary<Guid, int>();
Parallel.For(
0,
1000,
j =>
{
Guid currentGuid = gs.GetNextGuid();
if (!guids.TryAdd(currentGuid, j))
{
throw new ArgumentOutOfRangeException("GuidStore.GetNextGuid ThreadSafe Test Failed");
}
}); // Parallel.For
}
[TestMethod]
public void GetNextGuidTwoRunsProduceSameResultsTest()
{
Guid startingGuid = Guid.NewGuid();
GuidStore gsOne = new GuidStore(startingGuid);
/* The "key" of the ConcurrentDictionary must be unique, so this will catch any duplicates */
ConcurrentDictionary<Guid, int> setOneGuids = new ConcurrentDictionary<Guid, int>();
Parallel.For(
0,
1000,
j =>
{
Guid currentGuid = gsOne.GetNextGuid();
if (!setOneGuids.TryAdd(currentGuid, j))
{
throw new ArgumentOutOfRangeException("GuidStore.GetNextGuid ThreadSafe Test Failed");
}
}); // Parallel.For
gsOne = null;
GuidStore gsTwo = new GuidStore(startingGuid);
/* The "key" of the ConcurrentDictionary must be unique, so this will catch any duplicates */
ConcurrentDictionary<Guid, int> setTwoGuids = new ConcurrentDictionary<Guid, int>();
Parallel.For(
0,
1000,
j =>
{
Guid currentGuid = gsTwo.GetNextGuid();
if (!setTwoGuids.TryAdd(currentGuid, j))
{
throw new ArgumentOutOfRangeException("GuidStore.GetNextGuid ThreadSafe Test Failed");
}
}); // Parallel.For
bool equal = setOneGuids.Select(g => g.Key).OrderBy(i => i).SequenceEqual(
setTwoGuids.Select(g => g.Key).OrderBy(i => i), new GuidComparer<Guid>());
Assert.IsTrue(equal);
}
}
internal class GuidComparer<Guid> : IEqualityComparer<Guid>
{
public bool Equals(Guid x, Guid y)
{
return x.Equals(y);
}
public int GetHashCode(Guid obj)
{
return 0;
}
}
你这里有两个问题:
Dictionary.Add()
不是线程安全的。请改用ConcurrentDictionary.TryAdd()
。- 您的
GetNextGuid()
实现存在竞争条件,因为您 returningthis.LastGuidHolder
处于锁外,因此它可能会在 [=38] 之前被另一个线程修改=]ed.
一个明显的解决方案是将 return 移到锁内:
public Guid? GetNextGuid()
{
lock (objectlock)
{
if (this.LastGuidHolder.HasValue)
{
this.LastGuidHolder = Increment(this.LastGuidHolder.Value);
}
else
{
this.LastGuidHolder = Increment(this.StartingGuid);
}
return this.LastGuidHolder;
}
}
但是,我会将 return 类型更改为 Guid
- 它似乎对 return 和 Guid?
没有任何作用 - 这应该是隐藏在 class:
public Guid GetNextGuid()
{
lock (objectlock)
{
if (this.LastGuidHolder.HasValue)
{
this.LastGuidHolder = Increment(this.LastGuidHolder.Value);
}
else
{
this.LastGuidHolder = Increment(this.StartingGuid);
}
return this.LastGuidHolder.Value;
}
}
这是您使用 ConcurrentDictionary
的测试方法的一个版本:
static void Main(string[] args)
{
GuidStore gs = new GuidStore(Guid.NewGuid());
for (int i = 0; i < 1000; i++)
{
Console.WriteLine(i);
ConcurrentDictionary<Guid, int> guids = new ConcurrentDictionary<Guid, int>();
Parallel.For(0, 1000, j =>
{
Guid currentGuid = gs.GetNextGuid();
if (!guids.TryAdd(currentGuid, j))
{
Console.WriteLine("Duplicate found!");
}
}); // Parallel.For
}
Console.WriteLine("Press ENTER to Exit");
Console.ReadLine();
}
说了这么多,我不明白你为什么不只是使用 Guid.NewGuid()
...