多线程和关键部分的使用 - C++

发布于 2024-10-07 14:03:43 字数 206 浏览 3 评论 0原文

我对多线程应用程序中关键部分的正确使用有点困惑。在我的应用程序中,有几个在线程之间共享的对象(一些循环缓冲区和串行端口对象)。对这些对象的访问是否应该始终放置在关键部分内,还是仅在特定时间进行?我只在某些时候怀疑,因为当我尝试用 EnterCriticalSection /LeaveCriticalSection 包装每次使用时,我遇到了似乎是死锁的情况。如果您有任何见解,我们将不胜感激。谢谢。

I'm a little confused as to the proper use of critical sections in multithreaded applications. In my application there are several objects (some circular buffers and a serial port object) that are shared among threads. Should access of these objects always be placed within critical sections, or only at certain times? I suspect only at certain times because when I attempted to wrap each use with an EnterCriticalSection / LeaveCriticalSection I ran into what seemed to be a deadlock condition. Any insight you may have would be appreciated. Thanks.

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

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

发布评论

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

评论(2

苏别ゝ 2024-10-14 14:03:43

如果跨线程共享资源,并且其中一些线程读取而另一些线​​程写入,则必须始终保护该资源。

如果不了解更多关于代码的信息,就很难给出更多建议,但这里有一些需要记住的一般要点。

1) 关键部分保护资源,而不是进程

2) 在所有线程中以相同的顺序进入/离开临界区。如果线程 A 进入 Foo,然后进入 Bar,那么线程 B 必须以相同的顺序进入 Foo 和 Bar。如果你不这样做,你可以创建一场比赛。

3) 进出须按相反顺序进行。例如,由于您输入了 Foo,然后输入了 Bar,因此您必须先离开 Bar,然后才能离开 Foo。如果不这样做,可能会造成僵局。

4) 保持锁的时间尽可能短。如果在开始使用 Bar 之前已经使用完 Foo,请在抓住 Bar 之前释放 Foo。但你仍然必须牢记上面的排序规则。在每个同时使用 Foo 和 Bar 的线程中,您必须以相同的顺序获取和释放:

  Enter Foo
  Use Foo
  Leave Foo
  Enter Bar
  Use Bar
  Leave Bar

5) 如果您仅 99.9% 的时间读取而 0.1% 的时间写入,请不要自作聪明。即使你只是在阅读,你仍然必须进入暴击秒。这是因为您不希望在读取过程中开始写入。

6) 保持关键部分的粒度。每个关键部分应该保护一种资源,而不是多种资源。如果您将关键部分设置得太“大”,则可能会序列化您的应用程序或创建一组非常神秘的死锁或竞争。

If you share a resource across threads, and some of those threads read while others write, then it must be protected always.

It's hard to give any more advice without knowing more about your code, but here are some general points to keep in mind.

1) Critical sections protect resources, not processes.

2) Enter/leave critical sections in the same order across all threads. If thread A enters Foo, then enters Bar, then thread B must enter Foo and Bar in the same order. If you don't, you could create a race.

3) Entering and leaving must be done in opposite order. Example, since you entered Foo then entered Bar, you must leave Bar before leaving Foo. If you don't do this, you could create a deadlock.

4) Keep locks for the shortest time period reasonably possible. If you're done with Foo before you start using Bar, release Foo before grabbing Bar. But you still have to keep the ordering rules in mind from above. In every thread that uses both Foo and Bar, you must acquire and release in the same order:

  Enter Foo
  Use Foo
  Leave Foo
  Enter Bar
  Use Bar
  Leave Bar

5) If you only read 99.9% of the time and write 0.1% of the time, don't try to be clever. You still have to enter the crit sec even when you're only reading. This is because you don't want a write to start when your'e in the middle of a read.

6) Keep the critical sections granular. Each critical section should protect one resource, not multiple resources. If you make the critical sections too "big", you could serialize your application or create a very mysterious set of deadlocks or races.

子栖 2024-10-14 14:03:43

在支持 RAII 的临界区周围使用 C++ 包装器:

{
    CriticalSectionLock lock ( mutex_ );

    Do stuff...
}

即使抛出异常,锁的构造函数也会获取互斥锁,而析构函数会释放互斥锁。

尽量不要一次获得多个锁,并尽量避免在持有锁时调用类之外的函数;这有助于避免在不同的地方获得锁,从而减少发生死锁的可能性。

如果必须同时获取多个锁,请按地址对锁进行排序并按顺序获取它们。这样多个进程就可以以相同的顺序获得相同的锁,而无需协调。

对于 IO 端口,请考虑是否需要在输入的同时锁定输出 - 通常会遇到这样的情况:某些内容尝试写入,然后期望读取,反之亦然。如果您有两个锁,那么如果一个线程先写后读,而另一个线程先读后写,则可能会出现死锁。通常使用一个执行 IO 的线程和一个请求队列可以解决这个问题,但这比仅仅用锁包装调用要复杂一些,而且如果没有更多细节,我不能推荐它。

Use a C++ wrapper around the critical section which supports RAII:

{
    CriticalSectionLock lock ( mutex_ );

    Do stuff...
}

The constructor for the lock acquires the mutex and the destructor releases the mutex even if an exception is thrown.

Try not to gain more more than one lock at a time, and try to avoid calling functions outside of your class while holding locks; this helps avoid gaining locks in different places, so you tend to get fewer possibilities for deadlocks.

If you must gain more than one lock at the same time, sort the locks by their address and gain them in order. That way multiple processes gain the same locks in the same order without co-ordination.

With an IO port, consider whether you need to lock output at the same time as input - often you have a case where something tries to write, then expects to read, or visa-versa. If you have two locks, then you can get a deadlock if one thread writes then reads, and the other reads then writes. Often having one thread which does the IO and a queue of requests solves that, but that's a bit more complicated than just wrapping calls up with locks, and without much more detail I can't recommend it.

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