线程C++中的瓶颈

Bottleneck in Threads C++

本文关键字:C++ 线程      更新时间:2023-10-16

所以我只是想验证我的理解,并希望你们能够消除任何误解。因此,本质上,我有两个线程,它们使用相同的锁,并在持有锁时执行计算,但有趣的是,在锁内,我会导致线程短时间睡眠。对于这两个线程,任何一个线程的睡眠时间都会略有不同。由于锁的工作方式,速度较快的线程是否会被速度较慢的线程阻塞,因为它必须等待它完成?

例如:

Thread1() {
   lock();
   usleep(10)
   lock();
}

-

Thread2() {
   lock();
   sleep(100)
   lock();
}

现在,由于Thread2保持锁的时间更长,这将导致瓶颈。可以肯定的是,这个系统应该有一个关于谁得到锁的来回发生,对吧?

应该是:

Thread1 gets lock
Thread1 releases lock
Thread2 gets lock
Thread2 releases lock
Thread1 gets lock
Thread1 releases lock
Thread2 gets lock
Thread2 releases lock

等等,对吧?线程1应该永远无法在释放锁之后立即获取锁,对吗?

线程1应该永远无法在释放锁之后立即获取锁,对吗?

,线程1可以在释放锁之后立即重新获取锁,因为线程2仍然可以被挂起(由于调度程序而休眠)

此外,sleep仅保证线程将睡眠至少所需的量,它可以而且通常会更多。

在实践中,在计算值时您不会持有锁,您会获得锁,获得计算所需的值,解锁,计算它,然后再次获得锁,检查计算的旧值是否仍然有效/需要,然后存储/返回计算结果。为此,发明了std::future和原子数据类型。

这个系统应该有一个来回发生谁得到锁,对吧?

大多数情况下大多数时候是来回的,但有时Thread1可能会有两个锁定/解锁周期。这取决于您的调度程序,任何执行和周期都可能有所不同。

绝对没有什么可以阻止任何一个线程在释放锁后立即重新获取锁。我不知道你认为是什么阻止了这种情况的发生,但没有什么可以。

事实上,在许多实现中,已经运行的线程在获取锁方面比必须准备运行的线程具有优势。这是一个合理的优化,可以最大限度地减少上下文切换。

如果你把睡眠作为一种模拟工作的方式,并认为这代表了锁公平的一些现实世界问题,那么你错了。睡眠的线程会自动产生剩余的时间片,并且与耗尽时间片进行工作的线程的处理方式非常不同。如果这些线程真的在工作,最终一个线程会耗尽它的时间片。

根据您试图实现的目标,有几种可能性。

如果您希望线程按特定顺序运行,请查看此处。基本上有两个选项:
-一种是使用事件,其中一个线程向下一个线程发出信号,它已经完成了自己的工作,因此下一个可以启动
-另一种是有一个调度程序线程来处理事件或信号量的排序。

如果你希望你的线程独立运行,但有一个锁机制,其中试图获得锁的顺序是保留的,你可以看看这里。答案的最后一部分使用每个线程一个条件变量的队列似乎很好。

正如之前的回答和评论中所说,用睡眠来安排日程是个坏主意。此外,锁只是一种互斥机制,对执行顺序没有任何保障。锁通常用于防止对关键资源的并发访问,所以它应该这样做。临界截面越小越好
最后,是的,试图订购线程会造成"瓶颈"。在这种特殊的情况下,如果所有的计算都是在锁定的部分中进行的,那么线程将不会并行执行任何操作,因此您可能会质疑使用线程的实用性。

编辑:
还有一个警告:要小心,对于线程,这并不是因为它在你的机器上工作了10次(按照你想要的计划),它总是会工作的,尤其是当你更改任何上下文(机器、工作负载…)时。你必须通过设计来确保它。