线程安全工厂
Thread safe factory
我有一个工厂,它的工作是在需要时在每次调用时创建新实例。我的代码看起来像这样:
public class Factory
{
public object Get()
=> new object();
}
我尝试用这样的 nunit 测试来测试多线程部分:
public async Task Get_ThreadSaftyTests()
{
const int limit = 10_000
var concurrentCollection = new ConcurrentBag<object>();
var instance = new Factory();
var tasks = new List<Task>(limit);
for (int i = 0; i < limit; i++)
{
tasks.Add(Task.Factory.StartNew(() =>
{
concurrentCollection.Add(instance.Get());
}));
}
await Task.WhenAll(tasks);
var hashSet = new HashSet<long>();
foreach (var item in concurrentCollection)
{
hashSet.Add(item.GetHashCode());
}
Assert.AreEqual(limit, hashSet.Count);
}
这个测试大部分时间都通过了。确切地说是 4/5。这有点奇怪。
我试过在返回实例之前实现锁定,如下所示:
public class Factory
{
private static readonly Lazy<object> lockObject = new Lazy<object>(true);
public object Get()
{
lock (lockObject.Value)
{
return new object();
}
}
}
但是对于该实现,测试几乎从未通过。 1/5 运行测试通过。这里的问题是我做错了什么?
----------------------------更新---------------- ------------------
如果循环有 100 000 次迭代,则在这两种情况下每次测试都会失败。
如果循环有 6500 次迭代,则在两种情况下每次都会通过。
lock (lockObject.Value)
{
return new object();
}
在这种情况下锁什么都不做。调用构造函数可能是线程安全的,也可能不是线程安全的,但是框架中没有任何东西可以使创建对象成为非线程安全的。 IE。您将需要自己编写一个非线程安全的构造函数,然后由您来正确使用它。
但是:
var hashSet = new HashSet<long>();
foreach (var item in concurrentCollection)
{
hashSet.Add(item.GetHashCode());
}
GetHashCode
不能保证 return 是一个唯一的数字,它不能,因为引用可能是 64 位的,而 GetHashCode
return 是一个 32 位的数字。
修复应该很简单。更改为 HashSet<object>
并删除 .GetHashCode()
我有一个工厂,它的工作是在需要时在每次调用时创建新实例。我的代码看起来像这样:
public class Factory
{
public object Get()
=> new object();
}
我尝试用这样的 nunit 测试来测试多线程部分:
public async Task Get_ThreadSaftyTests()
{
const int limit = 10_000
var concurrentCollection = new ConcurrentBag<object>();
var instance = new Factory();
var tasks = new List<Task>(limit);
for (int i = 0; i < limit; i++)
{
tasks.Add(Task.Factory.StartNew(() =>
{
concurrentCollection.Add(instance.Get());
}));
}
await Task.WhenAll(tasks);
var hashSet = new HashSet<long>();
foreach (var item in concurrentCollection)
{
hashSet.Add(item.GetHashCode());
}
Assert.AreEqual(limit, hashSet.Count);
}
这个测试大部分时间都通过了。确切地说是 4/5。这有点奇怪。
我试过在返回实例之前实现锁定,如下所示:
public class Factory
{
private static readonly Lazy<object> lockObject = new Lazy<object>(true);
public object Get()
{
lock (lockObject.Value)
{
return new object();
}
}
}
但是对于该实现,测试几乎从未通过。 1/5 运行测试通过。这里的问题是我做错了什么?
----------------------------更新---------------- ------------------
如果循环有 100 000 次迭代,则在这两种情况下每次测试都会失败。
如果循环有 6500 次迭代,则在两种情况下每次都会通过。
lock (lockObject.Value)
{
return new object();
}
在这种情况下锁什么都不做。调用构造函数可能是线程安全的,也可能不是线程安全的,但是框架中没有任何东西可以使创建对象成为非线程安全的。 IE。您将需要自己编写一个非线程安全的构造函数,然后由您来正确使用它。
但是:
var hashSet = new HashSet<long>();
foreach (var item in concurrentCollection)
{
hashSet.Add(item.GetHashCode());
}
GetHashCode
不能保证 return 是一个唯一的数字,它不能,因为引用可能是 64 位的,而 GetHashCode
return 是一个 32 位的数字。
修复应该很简单。更改为 HashSet<object>
并删除 .GetHashCode()