在双缓冲多线程系统中执行指针交换

发布于 2024-08-21 04:40:07 字数 240 浏览 8 评论 0原文

当双缓冲要在线程之间共享的数据时,我使用了一个系统,其中一个线程从一个缓冲区读取,一个线程从另一个缓冲区读取,然后从第一个缓冲区读取。问题是,我该如何实现指针交换呢?我需要使用临界区吗?没有可用的互锁功能可以实际交换值。我不能让线程一从缓冲区一读取,然后在读取过程中开始从缓冲区二读取,这将导致应用程序崩溃,即使另一个线程没有开始写入它。

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

When double-buffering data that's due to be shared between threads, I've used a system where one thread reads from one buffer, one thread reads from the other buffer and reads from the first buffer. The trouble is, how am I going to implement the pointer swap? Do I need to use a critical section? There's no Interlocked function available that will actually swap values. I can't have thread one reading from buffer one, then start reading from buffer two, in the middle of reading, that would be appcrash, even if the other thread didn't then begin writing to it.

I'm using native C++ on Windows in Visual Studio Ultimate 2010 RC.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

神魇的王 2024-08-28 04:40:07

使用关键部分是公认的方法。只需在所有线程之间共享一个 CRITICAL_SECTION 对象,并在指针操作/缓冲区读取/写入代码周围对该对象调用 EnterCriticalSectionLeaveCriticalSection 即可。尝试尽快完成关键部分,并在关键部分之外留下尽可能多的代码。

即使您使用双重互锁交换技巧,您仍然需要一个关键部分或其他东西来同步您的线程,因此也可以将其用于此目的。

Using critical sections is the accepted way of doing it. Just share a CRITICAL_SECTION object between all your threads and call EnterCriticalSection and LeaveCriticalSection on that object around your pointer manipulation/buffer reading/writing code. Try to finish your critical sections as soon as possible, leaving as much code outside the critical sections as possible.

Even if you use the double interlocked exchange trick, you still need a critical section or something to synchronize your threads, so might as well use it for this purpose too.

淡笑忘祈一世凡恋 2024-08-28 04:40:07

对我来说,这听起来像是一个读者-作者-互斥类型的问题。

    [ ... but I mostly do embedded development so this may make no sense for a Windows OS.
    Actually, in an embedded OS with a priority-based scheduler, you can do this without any synchronization mechanism at all, if you guarantee that the swap is atomic and only allow the lower-priority thread to swap the buffers. ]

假设您有两个缓冲区 B1 和 B2,并且有两个线程 T1 和 T2。如果 T1 使用 B1 而 T2 使用 B2 也没关系。 “使用”是指读取和/或写入缓冲区。然后在某个时间,缓冲区需要交换,以便 T1 使用 B2,T2 使用 B1。您必须注意的是,交换是在两个线程都没有访问其缓冲区时完成的。

假设您只使用一个简单的互斥体。 T1 可以获取互斥锁并使用 B1。如果T2想要使用B2,它就必须等待互斥体。当 T1 完成时,T2 将解锁并与 B2 一起完成其工作。如果任一线程(或某些第三方线程)想要交换缓冲区,它也必须获取互斥锁。因此,仅使用一个互斥体就可以串行化对缓冲区的访问——不太好。

如果您使用读写器互斥锁,效果可能会更好。 T1 可以获取互斥锁上的读锁并使用 B1。 T2 还可以获取互斥锁上的读锁并使用 B2。当这些线程之一(或第三方线程)决定是时候交换缓冲区时,它必须对互斥锁进行写锁定。在没有更多的读锁之前,它将无法获取写锁。此时,它可以交换缓冲区指针,因为知道没有人在使用任一缓冲区,因为当互斥体上存在写锁定时,所有读锁定尝试都将被阻止。

This sounds like a reader-writer-mutex type problem to me.

    [ ... but I mostly do embedded development so this may make no sense for a Windows OS.
    Actually, in an embedded OS with a priority-based scheduler, you can do this without any synchronization mechanism at all, if you guarantee that the swap is atomic and only allow the lower-priority thread to swap the buffers. ]

Suppose you have two buffers, B1 and B2, and you have two threads, T1 and T2. It's OK if T1 is using B1 while T2 is using B2. By "using" I mean reading and/or writing the buffer. Then at some time, the buffers need to swap so that T1 is using B2 and T2 is using B1. The thing you have to be careful of is that the swap is done while neither thread is accessing its buffer.

Suppose you used just one simple mutex. T1 could acquire the mutex and use B1. If T2 wanted to use B2, it would have to wait for the mutex. When T1 completed, T2 would unblock and do its work with B2. If either thread (or some third-party thread) wanted to swap the buffers, it would also have to take the mutex. Thus, using just one mutex serializes access to the buffers -- not so good.

It might work better if you use a reader-writer mutex instead. T1 could acquire a read-lock on the mutex and use B1. T2 could also acquire a read-lock on the mutex and use B2. When one of those threads (or a third-party thread) decides it's time to swap the buffers, it would have to take a write-lock on the mutex. It won't be able to acquire the write-lock until there are no more read-locks. At that point, it can swap the buffer pointers, knowing that nobody is using either buffer because when there is a write-lock on the mutex, all attempts to read-lock will block.

橘香 2024-08-28 04:40:07

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

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

You have to build your own function to swap the pointers which uses a semaphore or critical section to control it. The same protection needs to be added to all users of pointers, since any code which reads a pointer which is in the midst of being modified is bad.

One way to manage this is to have all the pointer manipulation logic work under the protection of the lock.

多彩岁月 2024-08-28 04:40:07

为什么不能使用 InterlockedExchangePointer

编辑:好的,我明白你现在所说的,IEP 实际上并没有互相交换 2 个实时指针,因为它只通过引用获取单个值。

Why can't you use InterlockedExchangePointer ?

edit: Ok, I get what you are saying now, IEP doesn't actually swap 2 live pointers with each other since it only takes a single value by reference.

仅此而已 2024-08-28 04:40:07

看,我最初设计了线程,以便它们完全异步,并且在常规操作中不需要任何同步。但是,由于我在线程池中基于每个对象执行操作,如果给定的对象由于当前正在同步而无法读取,我可以在等待时执行另一个操作。从某种意义上说,我可以同时等待和操作,因为我有很多线程需要处理。

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

See, I did originally design the threads so that they would be fully asynchronous and don't require any synchronizing in their regular operations. But, since I'm performing operations on a per-object basis in a thread pool, if a given object is unreadable because it's currently being synced, I can just do another while I'm waiting. In a sense, I can both wait and operate at the same time, since I have plenty of threads to go around.

Create two critical sections, one for each of the threads.
While rendering, hold the render crit section. The other thread can still do what it likes to the other crit section though. Use TryEnterCriticalSection, and if it's held, then return false, and add the object in a list to be re-rendered later. This should allow us to keep rendering even if a given object is currently being updated.
While updating, hold both crit sections.
While doing game logic, hold the game logic crit section. If it's already held, that's no problem, because we have more threads than actual processors. So if this thread is blocked, then another thread will just use the CPU time and this doesn't need to be managed.

┊风居住的梦幻卍 2024-08-28 04:40:07

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

有常见的 Interlocked* 变体; InterlockedExchangeAcquire64()、InterlockedCompareExchange64() 等...

如果您需要在 XP 上运行,我会选择关键部分。当争用机会较低时,它们表现得相当好。

You haven't mentioned what your Windows platform limitations are, but if you don't need compatibility with older versions than Windows Server 2003, or Vista on the client side, you can use the InterlockedExchange64() function to exchange a 64 bit value. By packing two 32-bit pointers into a 64-bit pair structure, you can effectively swap two pointers.

There are the usual Interlocked* variantions on that; InterlockedExchangeAcquire64(), InterlockedCompareExchange64(), etc...

If you need to run on, say, XP, I'd go for a critical section. When the chance of contention is low, they perform quite well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文