在多个线程之间经常传递值的最佳架构是什么?

时间:2015-07-17 10:17:24

标签: c++ multithreading concurrency

我正在用C ++ 14编写一个应用程序,它由一个主线程和多个从属线程组成。主线程协调协调执行搜索的从属线程,每个线程探索搜索空间的一部分。从属线程有时会在搜索上遇到绑定。然后它将此绑定传递给主线程,主线程将绑定发送到所有其他从属线程,以便它们可以缩小搜索范围。

从属线程必须经常检查是否有可用的新绑定,可能是在循环的入口处。

将绑定通信到从属线程的最佳方法是什么?我可以考虑使用std::atomic<int>,但是我担心在循环中读取变量时会产生性能影响。

3 个答案:

答案 0 :(得分:0)

基本上你可以用你的架构打赌,对原始数据类型的单一写入是原子的。由于您只有一个编写器,如果使用volatile关键字来防止可能仅在本地缓存中对其执行更新的编译器优化,则程序不会中断。

然而,每个认真对待事情(tm)的人都会告诉你。看一下这篇文章就可以获得相当好的风险评估:http://preshing.com/20130618/atomic-vs-non-atomic-operations/

因此,如果您想要安全起见,我建议您遵循C ++标准。由于C ++标准即使对于最简单的操作也不能保证任何原子性,因此您仍然坚持使用std :: atomic。但老实说,我不认为这太糟糕了。当然有锁定,但你可以平衡阅读频率,早期了解新的边界。

为了防止轮询原子变量,您可以使用POSIX信号机制来通知从属线程更新(确保它与您编程的平台一起工作)。如果有利于表现,则需要看到。

答案 1 :(得分:0)

这里最简单的方法是IMO不要过分思考这个问题。只需对每个线程使用std::mutex,保护边界信息所在的std::queue。主线程等待每个孩子可以锁定的std::condition_variable,写入&#34 ;新界限&#34; queue,然后发出te cv信号,然后主线程将其唤醒并将值复制到每个孩子一次。正如您在问题中所说,在其循环的顶部,子线程可以检查其特定于线程的队列,以查看是否存在其他边界条件。

你实际上并不需要&#34;主线程&#34;在这。您可以让孩子直接写入所有其他孩子的队列(仍然是受互联网保护的),只要您小心避免死锁,它也可以这样工作。

所有这些类都可以在线程支持库中看到,带有合适的文档here

是的,有基于中断的处理方式,但在这种情况下,轮询相对便宜,因为它不是很多线程粉碎在一个互斥锁上,而是主要是线程特定的互斥锁和互斥锁锁定,检查,快速解锁都不是那么昂贵。你不是&#34;持有&#34;对他们很长一段时间,因此没关系。这真的是一个测试:你需要额外的无锁复杂性吗?如果它只有十几个(或更少)线程,那么可能不是。

答案 2 :(得分:-2)

这实际上非常简单。你只需要知道如何确保简单的解决方案不被破坏。所以,你需要的是两件事:
1.确保每次访问变量时都会在内存中写入/读取变量 2.确保你以原子的方式阅读它,这意味着你必须一次性读取全部值,或者如果没有自然地完成,则需要进行廉价测试来验证它。

要解决#1,你必须声明它是volatile。确保volatile关键字应用于变量本身。不是它的指针。

要解决#2,它取决于类型。在x86 / 64上,对整数类型的访问是原子的,只要它们与它们的大小对齐即可。也就是说,int32_t必须与4位边界对齐,并且int64_t必须与8字节边界对齐。

所以你可能会有这样的事情:

struct Params {
  volatile uint64_t bound __attribute__((aligned(8)));
};

如果你的bounds变量更复杂(一个结构)但仍然适合64位,你可以将它与uint64_t结合使用并使用与上面相同的属性和volatile。

如果它对于64位来说太大了,你需要某种锁来确保你没有读到半过时的值。适合您情况的最佳锁定(单个编写器,多个读取器)是序列锁定。序列锁只是一个volatile int,就像上面一样,用作数据的版本。它的值从0开始,每次更新时前进2。在更新受保护的值之前将其递增1,之后再次递增。最终结果是偶数是稳定状态,奇数是瞬态(值更新)。在读者中你这样做:
1.阅读版本。如果没有改变 - 返回
2.读到你得到偶数号 3.阅读受保护的变量
4.再次阅读该版本。如果你得到与以前相同的数字 - 你很好 5.否则 - 返回步骤2

这实际上是我下一篇文章中的主题之一。我将在C ++中实现它并让你知道。同时,您可以查看linux内核中的seqlock
另一个警告 - 你的内存访问之间需要编译器障碍,这样编译器就不会对它本不应该重新排序。这就是你在gcc中的表现方式:

asm volatile ("":::"memory");