Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 线程安全工厂_C#_Thread Safety_Locking_Factory - Fatal编程技术网

C# 线程安全工厂

C# 线程安全工厂,c#,thread-safety,locking,factory,C#,Thread Safety,Locking,Factory,我有一个工厂,它的工作是在需要时在每次调用时创建新实例。我的代码如下所示: public class Factory { public object Get() => new object(); } 我尝试使用以下nunit测试来测试多线程部分: public async Task Get_ThreadSaftyTests() { const int limit = 10_000 var concurrentCollection = new Con

我有一个工厂,它的工作是在需要时在每次调用时创建新实例。我的代码如下所示:

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);
}
public异步任务Get\u ThreadSaftyTests()
{
常数整数极限=10_000
var concurrentCollection=new ConcurrentBag();
var实例=新工厂();
var任务=新列表(限制);
对于(int i=0;i
{
concurrentCollection.Add(instance.Get());
}));
}
等待任务。何时(任务);
var hashSet=新hashSet();
foreach(concurrentCollection中的var项)
{
Add(item.GetHashCode());
}
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();
        }
    }
}
公共类工厂
{
private static readonly Lazy lockObject=new Lazy(true);
公共对象Get()
{
锁(lockObject.Value)
{
返回新对象();
}
}
}
但有了这种实现,测试几乎永远不会通过。1/5运行测试通过。这里的问题是我做错了什么

------------------------------更新-------------------------------

如果循环有100000次迭代,那么在这两种情况下,每次测试都会失败

    lock (lockObject.Value)
    {
        return new object();
    }
如果循环有6500次迭代,则在这两种情况下它每次都会通过

    lock (lockObject.Value)
    {
        return new object();
    }
锁在这种情况下不起作用。调用构造函数可能是线程安全的,也可能不是线程安全的,但是框架中没有任何东西使创建对象成为非线程安全的。也就是说,您需要自己编写一个非线程安全的构造函数,然后由您来正确使用它

然而:

var hashSet = new HashSet<long>();
foreach (var item in concurrentCollection)
{
    hashSet.Add(item.GetHashCode());
}
var hashSet=new hashSet();
foreach(concurrentCollection中的var项)
{
Add(item.GetHashCode());
}
GetHashCode
不能保证返回唯一的数字,因为引用可能是64位的,而
GetHashCode
返回32位的数字


解决方法应该很简单。更改为
HashSet
并删除
.GetHashCode()

这是很多任务,特别是在使用
StartNew
时,需要创建这么多任务是非常不寻常的。通常情况下,您只会创建尽可能多的单个处理器。测试完全没有意义。它没有任何用处。主要问题是,您将获得100000个对象,然后查看是否存在任何重复的哈希代码。这是——嗯,这是毫无意义的。无法保证100000个对象中每个对象的哈希代码都是唯一的。不过,这与线程安全无关——这只是一个非常奇怪的测试。我怀疑您应该测试的不是
HashSet
,而是
HashSet
。然后,您将意识到对象都是唯一的。作为旁注,一般来说,
Task.Run
Task.Factory.StartNew
。另外,
ConcurrentBag
是一个集合,几乎没有实际用途,您的示例不是其中之一。
ConcurrentQueue
更可取,因为它保留了插入项的顺序,但更好的做法是根本不使用并发集合,使用
列表
而不是
列表
,并且
var objects=wait Task.whalll(tasks)