2010-02-15 51 views
6

当双缓冲的数据这是由于被线程之间共享执行指针交换,我使用一个系统,其中一个线程从一个缓冲器读出,一个线程从另一个缓冲器读出和读出从第一个缓冲区。麻烦的是,我将如何实现指针交换?我需要使用关键部分吗?没有互锁功能可以实际交换数值。我不能让线程从缓冲区读取一个,然后开始从缓冲区读取两个,在读取的过程中,这将是appcrash,即使其他线程没有开始写入。在双缓冲的多线程系统

我在Visual Studio Ultimate 2010 RC的Windows上使用本机C++。

+0

你是什么意思?“没有互锁功能可以实际交换值。”?为什么你不能使用这些功能? – AndiDog 2010-02-15 20:33:20

回答

1

为什么不能使用InterlockedExchangePointer

编辑:好吧,我给你说的话,现在,IEP实际上不换2点彼此生活的指针,因为它只能通过引用,采用一个单值。

+0

如果您是InterlockedExchangePointer,则只能在呼叫中实际影响一个指针。这意味着为了交换所有三个指标,我需要三个电话 - 在中间留下一个空白。 InterlockedExchangePointer实际上是一个用词不当,它只会给给定值的给定指针。我预计它会交换一对指针(正是出于这个原因)。即使它的确如此工作,但如果不暂停这两个线程来执行更新,我仍然无法实际使用它,这是我真正想避免的。 – DeadMG 2010-02-15 20:39:23

+0

@DeadMG:'buf1 = InterlockedExchangePointer(&buf2,buf1);'如果你从当前使用buf1的线程调用它,我认为它会做你想做的。但我不完全确定你的意思是“所有三个指针”。 – Dan 2010-02-15 22:45:30

+0

我有一个来自渲染的缓冲区,一个来自游戏逻辑的缓冲区(所以它们都可以读取缓冲区),然后一个缓冲区以供游戏逻辑写入。问题是您调用的等价操作符不一定是原子 - InterlockedExchangePointer和buf1 =之间存在差距。即使没有,在这种情况下,仍然没有机制来确保我不在渲染过程中。 我认为在另一个答案中描述的读写互斥解决方案可能是最好的。 – DeadMG 2010-02-15 23:05:20

6

使用关键部分是可以接受的方式。只需在你的指针操作/缓冲区读/写代码周围共享一个CRITICAL_SECTION对象,并在该对象上调用EnterCriticalSectionLeaveCriticalSection。尝试尽快完成关键部分,尽可能在关键部分之外留下尽可能多的代码。

即使你使用双联锁交流技巧,你仍然需要一个临界区或东西同步线程,所以还不如将其用于这个目的了。

+0

我可能会误解OP的问题...... CRITICAL_SECTION不会导致序列化对缓冲区的所有访问,从而破坏了其中两个缓冲区的目的?我认为这个想法是有两个缓冲区,所以每个线程可以同时处理一些事情,偶尔缓冲区会被交换。 – Dan 2010-02-15 22:35:25

+0

那么有2个缓冲区仍然有助于在某些情况下。例如,考虑双缓冲交换链。 – Blindy 2010-02-15 23:11:42

+2

访问数据时,您不会持有它,只能交换它们。 – DeadMG 2010-02-15 23:44:33

1

您必须构建自己的函数来交换使用信号量或临界区的指针来控制它。需要将相同的保护添加到指针的所有用户,因为读取正在被修改的指针的任何代码是不好的

管理这个的一种方法是让所有指针操作逻辑在锁的保护下工作。

0

请参阅我最初设计的线程,以使它们完全异步,并且不需要在常规操作中进行任何同步。但是,因为我在线程池中以每个对象为基础执行操作,所以如果给定的对象由于当前正在同步而不可读,那么在等待时我可以再做一次。从某种意义上说,我可以同时等待和操作,因为我有很多线索可以解决。

创建两个关键部分,每个线程一个。 渲染时,按住渲染暴击部分。另一个线程仍然可以做到它喜欢的其他爆击部分。使用TryEnterCriticalSection,如果它被保留,则返回false,并将该对象添加到列表中以稍后重新呈现。这应该允许我们继续渲染,即使给定的对象当前正在更新。 更新时,同时保留两个暴击部分。 在进行游戏逻辑的同时,保持游戏逻辑暴击部分。如果已经存在,那就没有问题了,因为我们的线程比实际的处理器多。所以如果这个线程被阻塞,那么另一个线程将只使用CPU时间,这不需要管理。

+0

我不认为你可以维持这种零同步方法的时间太长。你显然已经在这里遇到了一堵砖墙,并且在你的代码中也可能存在一些bug,但是一般来说,更新游戏状态需要某种锁定来使渲染状态中间渲染不会失效。 – Blindy 2010-02-15 21:15:09

+0

什么代码? :P我现在只提供简单的几个文本对象来测试多线程设计。我真的不想写一个渲染器,然后不得不返回并再次写入多线程。 我会去看看我现在可以用锁来完成什么。 – DeadMG 2010-02-15 21:29:26

0

您还没有提及您的Windows平台限制,但是如果您不需要与Windows Server 2003或客户端上的Vista相比较旧版本的兼容性,则可以使用InterlockedExchange64()函数来交换64位值。通过将两个32位指针打包为64位对结构,可以有效地交换两个指针。

有通常的Interlocked * variantions; InterlockedExchangeAcquire64(),InterlockedCompareExchange64()等...

如果你需要运行,比如XP,我会去一个关键部分。当争用的可能性很低时,它们表现相当好。

+0

我很乐意考虑不在XP上运行。但是,它并没有解决交换中间渲染指针的问题。 – DeadMG 2010-02-15 21:24:40

4

这听起来像是一个读写器互斥类型的问题。

    [...但我主要是做嵌入式开发,所以这可能没有意义的Windows操作系统。 实际上,在基于优先级的调度程序的嵌入式操作系统中,如果您保证交换是原子性的并且只允许优先级较低的线程交换缓冲区,则可以根本不使用任何同步机制。 ]

假设您有两个缓冲区B1和B2,并且您有两个线程T1和T2。如果T1正在使用B1而T2正在使用B2,则可以。 “使用”是指读取和/或写入缓冲区。然后在某个时候,缓冲区需要交换,以便T1使用B2,而T2使用B1。你必须小心的是,交换是在两个线程都不访问缓冲区的情况下完成的。

假设您只使用了一个简单的互斥锁。 T1可以获得互斥体并使用B1。如果T2想要使用B2,它必须等待互斥锁。当T1完成时,T2将解除阻塞并与B2一起工作。如果任一线程(或某个第三方线程)想要交换缓冲区,则它也必须采用互斥锁。因此,只使用一个互斥量序列化访问缓冲区 - 不太好。

如果您改用读写器互斥锁,它可能会更好。 T1可以获得对互斥锁的读锁,并使用B1。 T2也可以获得对互斥锁的读锁,并使用B2。当其中一个线程(或第三方线程)决定交换缓冲区时,它将不得不对互斥锁执行写入锁定。直到没有更多的读锁,它将无法获得写锁。此时,它可以交换缓冲区指针,知道没有人使用任何一个缓冲区,因为当互斥锁上存在写入锁定时,所有尝试读取锁定的操作都将被阻止。

+0

一个有趣的解决方案 - 几乎和我以前一样,除了更清洁。但是,我无法在MSDN上找到关于这种互斥体的任何文档。它们是WinAPI的固有组成部分,还是我必须自己编写代码? – DeadMG 2010-02-15 23:02:37

+0

打败我。我谷歌'读者作家互斥窗口',并发现这一点:http://msdn.microsoft.com/en-us/magazine/cc163599.aspx - 有帮助吗?谷歌没有'windows'来读理论...... – Dan 2010-02-16 01:52:28

+0

如果传入数据的顺序很重要,那么仍然需要同步。无论读者/写者互斥,阅读者在继续之前都必须等待新的数据到达。 – 2011-03-25 15:40:19