Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/311.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# ConcurrentBag与自定义线程安全列表_C#_Multithreading_Concurrency_.net 4.5_Readerwriterlockslim - Fatal编程技术网

C# ConcurrentBag与自定义线程安全列表

C# ConcurrentBag与自定义线程安全列表,c#,multithreading,concurrency,.net-4.5,readerwriterlockslim,C#,Multithreading,Concurrency,.net 4.5,Readerwriterlockslim,我有一个.NET4.5单实例WCF服务,它在一个列表中维护一个项目集合,该列表将同时具有读卡器和写卡器,但读卡器远远多于写卡器 我目前正在决定是使用BCLConcurrentBag还是使用我自己的自定义泛型类(它扩展了IList并封装了BCLReaderWriterLockSlim,因为它更适合多个并发读卡器) 在测试这些实现时,我通过模拟一个并发场景(仅运行一个Sum Linq查询)和100个writer(向列表中添加项),发现了许多性能差异 对于我的性能测试,我有一个任务列表: List&l

我有一个.NET4.5单实例WCF服务,它在一个列表中维护一个项目集合,该列表将同时具有读卡器和写卡器,但读卡器远远多于写卡器

我目前正在决定是使用BCL
ConcurrentBag
还是使用我自己的自定义泛型类(它扩展了
IList
并封装了BCL
ReaderWriterLockSlim
,因为它更适合多个并发读卡器)

在测试这些实现时,我通过模拟一个并发场景(仅运行一个Sum Linq查询)和100个writer(向列表中添加项),发现了许多性能差异

对于我的性能测试,我有一个任务列表:

List<Task> tasks = new List<Task>();
测试1中ConcurrentBag的效率比我的定制实现好,而测试2中ConcurrentBag的效率比我的定制实现差,因此我发现很难决定使用哪一个

问题1。当我唯一要改变的是列表中任务的顺序时,为什么结果会如此不同

问题2。有没有更好的办法来改变我的考试,使它更公平

为什么结果如此不同,而我唯一要改变的是 列表中任务的顺序

我最好的猜测是,
Test#1
实际上并不读取项目,因为没有什么可读取的。任务执行的顺序为:

  • 从共享池读取1M次并计算总和
  • 写入共享池100次
  • 你的
    测试#2
    混合了读写,这就是为什么我猜你会得到不同的结果

    有没有更好的办法来改变我的考试,使它更公平

    在开始任务之前,请尝试将任务的顺序随机化。可能很难重现相同的结果,但您可能会更接近真实世界的使用情况


    最后,您的问题是关于乐观并发(
    Concurrent*
    classes)和悲观并发(基于锁)的区别。根据经验,当您同时访问共享资源的机会较低时,您更喜欢乐观并发。当同时访问的机会较高时,选择悲观的

    谢谢你的意见!在测试1中,更改顺序使写入优先,读取第二,这会在性能上产生巨大的差异。ConcurrentBag将在9秒左右推出,而我的定制ThreadSafeList是1300ms。我相信测试2是一个很好的尝试,可以将任务的顺序随机化?我认为,由于WCF服务是一个发布-订阅服务器,订阅(写入列表)很少,但多条消息的并发广播(从列表中读取)很频繁,因此很有可能出现悲观的并发情况。我会使用Fisher-Yates Durstenfeld shuffle之类的方法。见实施。当您启动任务时,它可能看起来像:
    tasks.Shuffle().ForEach(task=>task.start())
    看起来您的实现更好(但仅针对您的特定用例),ConcurrentBag针对写入包中的线程和从包中读取的线程进行了优化。如果您没有从包中读取相同的线程,请切换到ConcurrentQueue。阅读,在您的示例中,您使用的是“纯生产者-消费者场景”,每个线程要么只读取,要么只写入包,没有一个线程在同一个线程中读写。我同意-这让我问你什么时候会使用ConcurrentBag,因为根据我的理解,你不能在同一个线程上同时读写……你能吗?因此,我会让每个线程维护自己的列表。当然,将包命名为并发集合的一部分是个坏主意,因为它在多线程场景中效率低下?…除非我遗漏了什么!Bag适用于这样的情况:您有一群工作线程,它们将工作全部转储到一个池中,然后在完成后从同一个池中拉出。如果一个工作线程最终完成了他输入的所有工作(它在内部存储数据的方式将返回线程首先输入的数据),他将开始从其他线程“窃取”工作。或者另一个常见用途可能是工作线程尝试从池中提取,发现没有项目,创建新项目,然后,处理完资源后,添加新项/将旧项返回到池中。下一次同一线程需要池中的项目时,速度会更快,但是如果另一个线程访问池,它可以使用第一个threads对象,而无需创建自己的对象。关于这种行为的一个很好的现实例子是。我认为,由于你的广泛回答,这有理由提出自己的问题。我认为记住这一点的最好方法是连接池示例。
    tasks.AddRange(Enumerable.Range(0, 1000000).Select(n => new Task(() => { temp.Where(t => t < 1000).Sum(); })).ToArray());
    tasks.AddRange(Enumerable.Range(0, 100).Select(n => new Task(() => { temp.Add(n); })).ToArray());
    
    foreach (var item in Enumerable.Range(0, 1000000))
    {
        tasks.Add(new Task(() => temp.Where(t => t < 1000).Sum()));
        if (item % 10000 == 0)
            tasks.Add(new Task(() => temp.Add(item)));
    }
    
    Stopwatch watch = new Stopwatch();
    watch.Start();
    tasks.ForEach(task => task.Start());
    Task.WaitAll(tasks.ToArray());
    watch.Stop();
    Console.WriteLine("Time: {0}ms", watch.Elapsed.TotalMilliseconds);