什么是“忙等待”与“睡眠”的权衡?

时间:2009-07-10 03:51:10

标签: c linux operating-system

这是我之前的问题的扩展

How does blocking mode in unix/linux sockets works?

我现在从Internet收集的内容,调用阻塞调用的所有进程都会进入休眠状态,直到调度程序找到解除阻塞的原因。原因可以从缓冲区空白到缓冲区满,也可以是任何其他条件。

但是,这可以成为实时的有效方式,让我们说硬/实时应用程序?由于当解除阻塞条件成立时,进程未被解除阻塞,而是当调度程序给出他的CPU切片时,并且解除阻塞条件都为真。

好像你想要一个响应式解决方案,我不这个“旋转锁”或“忙等待”是正确的方法,CPU切片被浪费,整个系统将无法响应或可能很差响应性。

有人可以清除这些相互矛盾的想法。

6 个答案:

答案 0 :(得分:39)

在调度程序唤醒你之前进入睡眠是正常/首选的事情。

旋转(等待的替代方式,不睡觉)不太常见,并具有以下效果:

  • 保持CPU忙碌,并阻止其他线程使用CPU(除非旋转线程完成其时间片并被禁止使用)

  • 当你正在等待的事情发生时,可以停止旋转(因为你不断检查那个事件,而你不需要花时间来唤醒它,因为你已经醒了)

  • 不调用进入睡眠状态并再次唤醒所需的CPU结构

如果延迟的长度非常短,则旋转可以比进入睡眠更有效(更少的总CPU)(例如,如果延迟仅在执行100时延迟) CPU指令)。

答案 1 :(得分:4)

旋转锁定会烧毁CPU和轮询的资源路径,以便在未发生所需事件时继续浪费资源。

阻止操作最重要的不同之处在于保留CPU和相关资源路径,并在预期所需事件的资源上​​安装某种形式的wait

在多任务处理或多线程/处理器环境中(通常情况下很长一段时间),在没有到达所需事件的情况下可能进行其他操作时,烧毁CPU和资源访问路径会导致处理能力的极大浪费和时间。

当我们有一个超线程系统(就像我认为你在你的问题中指的那样)时,重要的是要注意切片CPU线程的粒度非常高。我会伸出脖子观察所有事件 - 你会倾向于阻止它 - 会花费足够的时间来产生,补偿他们在解锁之前必须等待的小时间片。


我认为J-16的观点是睡眠(阻塞)线程在阻塞状态下未使用其代码和数据空间的情况。这可能会使系统放弃资源(如数据/代码缓存),然后在释放块时需要重新填充资源。因此,在条件允许的情况下,区块可能会造成更多的资源浪费 这也是一个有效的说明,应在设计和实施中进行检查 但是,在大多数情况下,阻塞通常比自旋锁更好。

答案 2 :(得分:1)

如果在您的应用程序的用例中,上下文切换比吃几个CPU周期更昂贵,因为您的条件将保证在短时间内得到满足 时间,然后忙着等待可能对你有好处。

否则,您可以通过休眠或cond_wait()强行放弃CPU。

我可以想到强制上下文切换的另一个场景如下:

while(condition)
    sleep(0);

答案 3 :(得分:0)

首先,您有一种误解:

阻止呼叫不是“忙等待”或“旋转锁定”。阻塞调用是可以访问的 - 这意味着CPU可以处理其他任务,不会浪费cpu。

关于阻止来电的问题

阻止调用更容易 - 它们易于理解,更易于开发,更易于调试。

但他们是资源匮乏。如果你不使用线程,它会阻止其他客户端;如果使用线程,每个线程将占用内存和其他系统资源。即使你有足够的内存,切换线程也会使缓存变冷并降低性能。

这是一种权衡 - 更快的开发和可维护性?或可扩展性。

答案 4 :(得分:0)

我将努力做到这一点,因为这里通过其他答案提供了足够的解释,是的,从所有这些答案中学习,我认为应该是完整的图片。 ---

根据我的权衡,应该在系统的响应性和吞吐量之间进行权衡。

响应能力 - 可以从两个角度考虑

  • 了解所有系统响应能力,
  • 特定或每个流程的响应

我认为对于系统的响应性,阻止呼叫是最好的方法。因为它给处于就绪队列中的其他进程的CPU,当阻塞调用处于阻塞状态时。

当然,对于特定进程或每进程响应,我们将考虑忙等待/自旋锁模型。

现在,再次提高整体系统响应能力我们无法减少调度程序的时间片(细粒度),因为这会在上下文切换中浪费太多的CPU资源。因此系统的吞吐量将大幅减少。当然,很明显阻塞模型会增加系统的吞吐量,因为被阻塞的调用不会消耗CPU切片并将其提供给就绪队列中的另一个/下一个进程。

我认为最好的办法是 - 设计一个系统,每个流程的响应能力都在考虑,而不会影响整体响应能力和吞吐量 - 通过实施基于优先级的调度程序,考虑优先级倒置问题,如果增加复杂性不会打扰你:)。

答案 5 :(得分:-2)

//改编的ASPI原始源代码......

    DWORD startStopUnit (HANDLE handle, BOOL bLoEj, BOOL bStart)
    {
       DWORD  dwStatus;
       HANDLE heventSRB;
       SRB_ExecSCSICmd s;





    //here
       heventSRB = CreateEvent (NULL, TRUE, FALSE, NULL);

       memset (&s, 0, sizeof (s));

       s.SRB_Cmd = SC_EXEC_SCSI_CMD;
       s.SRB_HaID = 0;
       s.SRB_Target = 0;
       s.SRB_Lun = 0;
       s.SRB_Flags = SRB_EVENT_NOTIFY;
       s.SRB_SenseLen = SENSE_LEN;
       s.SRB_CDBLen = 6;
       s.SRB_PostProc = (LPVOID) heventSRB;
       s.CDBByte[0] = 0x1B;
       s.CDBByte[4] |= bLoEj ? 0x02 : 0x00;
       s.CDBByte[4] |= bStart ? 0x01 : 0x00;

       ResetEvent (heventSRB);
       dwStatus = SPTISendASPI32Command (handle,(LPSRB) & s);
       if (dwStatus == SS_PENDING)
         {
//and here, don´t know a better way to wait for something to finish without processor cicles
        WaitForSingleObject (heventSRB, DEFWAITLEN);
         }
       CloseHandle (heventSRB);

       if (s.SRB_Status != SS_COMP)
         {
    printf("Erro\n");
        return SS_ERR;
         }

       printf("nao Erro\n");
       return s.SRB_Status;
    }