Multithreading 交换缓冲区对并发访问的影响
考虑一个有两个线程的应用程序,生产者和消费者 两个线程的运行频率大致相同,一秒钟内运行多次 两个线程都访问同一个内存区域,其中生产者写入内存,消费者读取当前数据块并对其执行操作,而不会使数据无效 一个经典的方法是:Multithreading 交换缓冲区对并发访问的影响,multithreading,concurrency,thread-safety,Multithreading,Concurrency,Thread Safety,考虑一个有两个线程的应用程序,生产者和消费者 两个线程的运行频率大致相同,一秒钟内运行多次 两个线程都访问同一个内存区域,其中生产者写入内存,消费者读取当前数据块并对其执行操作,而不会使数据无效 一个经典的方法是: int[]共享数据; //线程生成器经常调用 无效写值(int[]数据) { 锁(共享数据) { 复制(数据、共享数据、长度); } } //线程使用者经常调用 void WriteValues() { int[]数据; 锁(共享数据) { 复制(共享数据、数据、长度); } 剂量测
int[]共享数据;
//线程生成器经常调用
无效写值(int[]数据)
{
锁(共享数据)
{
复制(数据、共享数据、长度);
}
}
//线程使用者经常调用
void WriteValues()
{
int[]数据;
锁(共享数据)
{
复制(共享数据、数据、长度);
}
剂量测定(数据);
}
如果我们假设Array.Copy
需要时间,那么这段代码会运行得很慢,因为生产者在复制过程中总是要等待消费者,反之亦然
解决此问题的一种方法是创建两个缓冲区,一个由使用者访问,另一个由生产者写入,并在写入完成后交换缓冲区
int[]frontBuffer;
int[]backBuffer;
//线程生成器经常调用
无效写值(int[]数据)
{
锁(后缓冲)
{
Array.Copy(数据、backBuffer、长度);
int[]temp=frontBuffer;
前缓冲区=后缓冲区;
backBuffer=温度;
}
}
//线程使用者经常调用
void WriteValues()
{
int[]数据;
int[]currentFrontBuffer=frontBuffer;
锁定(currentForntBuffer)
{
复制(currentFrontBuffer、数据、长度);
}
剂量测定法(currentForntBuffer);
}
现在,我的问题是:
WriteValues()
方法从frontBuffer
读取数据时没有强制缓存刷新的锁或内存屏障,因此无法保证该变量将使用主内存中的新值进行更新。然后可能会从本地寄存器或CPU缓存中连续读取该实例的过时缓存值。我不确定编译器/JIT是否会根据局部变量推断缓存刷新,也许有更具体的知识的人可以在这方面发言
即使这些值没有过时,您也可能会遇到比您希望的更多的争用。例如
- 线程A调用
writeValue()
- 线程A在
中锁定实例并开始复制frontBuffer
- 线程B调用
writeValue(int[])
- 线程B写入数据,将当前锁定的
实例移动到frontBuffer
中backBuffer
- 线程B调用
writeValue(int[])
- 线程B等待
的锁,因为线程A仍然拥有它backBuffer
您可能能够,但我不建议您尝试,除非您非常熟悉多线程、缓存一致性和潜在的编译器/JIT优化。锁定很可能适合您的情况,您(以及其他阅读您的代码的人)可以更轻松地进行推理和维护。谢谢您的回答。首先,我向你保证,我的问题是假设性的。我知道正确锁定的好处,不想做坏事。然而,我对第二个示例中的代码不安全的说法感到好奇。你能告诉我,在代码中的什么地方,在什么情况下可能会发生不安全的操作吗?@Emiswelt哎呀,我看代码时脑子里有Friday,但看得不够仔细。我已经更新了我的答案,以解决我看到的问题。谢谢。我没想到你说的点心问题。