【发布时间】:2020-02-22 11:04:31
【问题描述】:
我有一个Buffer 类,它实现了在设计时考虑到多线程的double buffer pattern:
class Buffer {
public:
void write(size_t idx, float value) {
std::lock_guard<std::mutex> lk(mut);
(*next)[idx] = value; // write to next buffer
}
float read(size_t idx) const {
std::lock_guard<std::mutex> lk(mut);
return (*current)[idx]; // read from current buffer
}
void swap() noexcept {
std::lock_guard<std::mutex> lk(mut);
std::swap(current, next); // swap pointers
}
Buffer() : buf0(32), buf1(32), current(&buf0), next(&buf1) { }
private:
std::vector<float> buf0, buf1; // two buffers
std::vector<float> *current, *next;
mutable std::mutex mut;
};
这个类包含两个缓冲区。缓冲区总是通过指针访问:
- 读取的缓冲区,由
current指针指定。 - 写入的缓冲区,由
next指针指定。
将有两个线程:一个更新线程将调用write 方法写入下一个缓冲区,一个读取线程将调用read 方法从当前缓冲区读取。当更新线程完成后,它会调用swap 来交换指向缓冲区的指针。
缓冲区的交换必须以原子方式完成,因此我必须在每个方法中锁定互斥锁(创建 lk 对象)。
问题是在每个方法中锁定互斥锁会阻止两个线程同时访问它们对应的缓冲区。但是这两个缓冲区是独立的:如果一个线程修改一个缓冲区,而另一个线程读取另一个缓冲区,则没有问题。
我想允许更新线程在读取线程读取其相应缓冲区的同时修改其缓冲区。有什么方法可以实现吗?
【问题讨论】:
-
可能有两个互斥锁:一个只用于读取,另一个用于写入。
swap()必须锁定这两个互斥锁。 -
谁在你的代码中调用
swap()?读者还是作者?虽然你有锁可以使交换原子和读写两个缓冲区的单个元素成为原子的,但没有什么可以真正使访问缓冲区作为一个整体原子的。 -
@G.Sliepen 作者在完成后调用 swap()。
-
这意味着如果一个读者想要,例如,通过写
float diff = buffer.read(1) - buffer.read(0)来获得两个值的差异,那么虽然这两个读是原子的,但写者可以在两者之间调用buffer.swap()两读。最终结果可能不是您所期望的。
标签: c++ multithreading locking