什么能比并发集合更好地解决这种多线程场景
What could solves this multi-threaded scenario better than Concurrent collections
我有一个持久的 B+ 树,多个线程正在读取树的不同块并对读取的数据执行一些操作。 有趣的部分: 每个线程都会产生一组结果,作为最终用户,我希望在一个地方看到所有结果。 我做什么: 一个 ConcurentDictionary 并且所有线程都在写入它。
这样一切都很顺利。但是应用程序是时间紧迫的,多一秒钟意味着完全不满意。由于线程安全开销,ConcurentDictionary 本质上比 Dictionary 慢。
我可以使用字典,然后每个线程将结果写入不同的字典。但是我会遇到合并不同词典的问题。
.
我的问题:
- 并发收集是否适合我的场景?
- 如果不是(1),那么我将如何最佳地合并不同的词典。鉴于此,(a) 逐个复制项目和 (b) LINQ 是已知的解决方案,并不像预期的那样最佳:)
- 如果不是(2) ;-)你会建议什么?
.
快速信息:
- #Thread = 处理器计数。该应用程序可以在标准笔记本电脑(即 4 个线程)或高端服务器(即 <32 个线程)上 运行
- 项目计数。这棵树通常拥有超过 1.0E+12 个项目。
你考虑过异步持久化吗?
你的场景允许吗?
您可以绕过单独线程池中的队列(创建线程池可以避免为每个请求创建(子)线程的开销),并且您可以在不影响响应时间的情况下处理合并逻辑。
从您的时间来看,结果字典的 locking/building 似乎每个线程占用 3700 毫秒,而实际处理逻辑仅占用 300 毫秒。
我建议您让每个线程创建自己的本地结果字典作为实验。然后你可以看到构建字典所花费的时间与跨线程锁定的效果相比有多少。
如果构建本地词典的时间超过 300 毫秒,则无法满足您的时间限制。因为没有任何锁定或任何合并结果的尝试,它已经花费了太长时间。
更新
看来您可以在进行过程中支付合并费用,锁定会导致线程闲置相当长的时间,或者在 post-processing 合并中支付费用.但核心问题是锁定意味着您没有充分利用可用的 CPU.
从内核获得最大性能的唯一真正解决方案是使用非阻塞字典实现,该实现也是线程安全的。我找不到 .NET 实现,但确实找到了一篇研究论文,其中详细介绍了一种算法,表明它是可能的。
正确实施这样的算法并不简单,但会很有趣!
我有一个持久的 B+ 树,多个线程正在读取树的不同块并对读取的数据执行一些操作。 有趣的部分: 每个线程都会产生一组结果,作为最终用户,我希望在一个地方看到所有结果。 我做什么: 一个 ConcurentDictionary 并且所有线程都在写入它。
这样一切都很顺利。但是应用程序是时间紧迫的,多一秒钟意味着完全不满意。由于线程安全开销,ConcurentDictionary 本质上比 Dictionary 慢。
我可以使用字典,然后每个线程将结果写入不同的字典。但是我会遇到合并不同词典的问题。
.
我的问题:
- 并发收集是否适合我的场景?
- 如果不是(1),那么我将如何最佳地合并不同的词典。鉴于此,(a) 逐个复制项目和 (b) LINQ 是已知的解决方案,并不像预期的那样最佳:)
- 如果不是(2) ;-)你会建议什么?
.
快速信息:
- #Thread = 处理器计数。该应用程序可以在标准笔记本电脑(即 4 个线程)或高端服务器(即 <32 个线程)上 运行
- 项目计数。这棵树通常拥有超过 1.0E+12 个项目。
你考虑过异步持久化吗?
你的场景允许吗?
您可以绕过单独线程池中的队列(创建线程池可以避免为每个请求创建(子)线程的开销),并且您可以在不影响响应时间的情况下处理合并逻辑。
从您的时间来看,结果字典的 locking/building 似乎每个线程占用 3700 毫秒,而实际处理逻辑仅占用 300 毫秒。
我建议您让每个线程创建自己的本地结果字典作为实验。然后你可以看到构建字典所花费的时间与跨线程锁定的效果相比有多少。
如果构建本地词典的时间超过 300 毫秒,则无法满足您的时间限制。因为没有任何锁定或任何合并结果的尝试,它已经花费了太长时间。
更新
看来您可以在进行过程中支付合并费用,锁定会导致线程闲置相当长的时间,或者在 post-processing 合并中支付费用.但核心问题是锁定意味着您没有充分利用可用的 CPU.
从内核获得最大性能的唯一真正解决方案是使用非阻塞字典实现,该实现也是线程安全的。我找不到 .NET 实现,但确实找到了一篇研究论文,其中详细介绍了一种算法,表明它是可能的。
正确实施这样的算法并不简单,但会很有趣!