考虑一个具有两个线程的应用程序,生产者和消费者。
两个线程的运行频率大致相同,在一秒钟内运行多次。
两个线程访问同一个内存区域,生产者写入内存,消费者读取当前数据块并对其进行处理,而不会使数据无效。
一种经典的方法是这样的:
int[] sharedData;
//Called frequently by thread Producer
void WriteValues(int[] data)
{
lock(sharedData)
{
Array.Copy(data, sharedData, LENGTH);
}
}
//Called frequently by thread Consumer
void WriteValues()
{
int[] data;
lock(sharedData)
{
Array.Copy(sharedData, data, LENGTH);
}
DoSomething(data);
}
如果我们假设这Array.Copy
需要时间,那么这段代码会运行得很慢,因为在复制过程中生产者总是必须等待消费者,反之亦然。
解决此问题的一种方法是创建两个缓冲区,一个由消费者访问,一个由生产者写入,并在写入完成后立即交换缓冲区。
int[] frontBuffer;
int[] backBuffer;
//Called frequently by thread Producer
void WriteValues(int[] data)
{
lock(backBuffer)
{
Array.Copy(data, backBuffer, LENGTH);
int[] temp = frontBuffer;
frontBuffer = backBuffer;
backBuffer = temp;
}
}
//Called frequently by thread Consumer
void WriteValues()
{
int[] data;
int[] currentFrontBuffer = frontBuffer;
lock(currentForntBuffer)
{
Array.Copy(currentFrontBuffer , data, LENGTH);
}
DoSomething(currentForntBuffer );
}
现在,我的问题:
- 如第二个示例所示,锁定是否安全?还是引用的变化会带来问题?
- 第二个示例中的代码会比第一个示例中的代码执行得更快吗?
- 有没有更好的方法来有效解决上述问题?
- 有没有办法在没有锁的情况下解决这个问题?(即使我认为不可能)
注意:这不是典型的生产者/消费者问题:消费者可以在生产者再次写入之前多次读取值 - 旧数据保持有效,直到生产者写入新数据。