什么是“忙等待”与“睡眠”之间的折衷?

这是对我以前的问题的延伸

unix / linux套接字中的阻塞模式是如何工作的?

我现在从互联网上收集到的所有调用阻塞调用的进程都会进入hibernate状态,直到调度程序find解除阻塞的原因。 原因可以从缓冲区空到缓冲区满到任何其他条件。

但是,那么这可以成为实时的高效方式,可以说硬/实时的应用程序? 由于在解除阻塞条件成立的情况下进程未被解除阻塞,而是当调度器给予他的CPU片,并且解除阻塞条件都是成立的。

就好像你想要一个响应式的解决scheme,我不这样做“自旋锁”或“繁忙的等待”是正确的方式来做到这一点,CPU切片被浪费,整个系统将得到不响应或可能反应迟钝。

有人可以请清楚这个矛盾的想法。

Solutions Collecting From Web of "什么是“忙等待”与“睡眠”之间的折衷?"

要睡觉,直到调度员醒来,你才是正常的/喜欢的事情。

纺纱(等待,不睡觉的替代方式)不太常用,并具有以下效果:

  • 使CPU保持繁忙状态,并防止其他线程使用CPU(直到/除非旋转的线程完成其时间片并被预加工)

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

  • 不会调用进入睡眠状态并重新唤醒所需的CPU结构

如果延迟时间非常短(例如延迟时间只要执行100条CPU指令所需的时间),那么纺纱可以比睡眠更有效(总CPU更少)。

旋转锁定将烧毁CPU和轮询的资源路径,以防止继续浪费资源,而不会发生所需的事件。

Blocking操作最重要的不同之处在于,将CPU和关联的资源路径留出,并在预期的事件所在的资源上安装某种形式的wait

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

当我们有一个超线程系统(就像我认为你指的是你的问题),注意到CPU线程被分割的粒度非常高是很重要的。 我会坚持我的脖子,也观察到所有的事件 – 你会倾向于阻止 – 将花费足够的时间,补偿他们不得不等待的额外等待时间之前解锁。


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

如果在应用程序的用例中,上下文切换比吃少量的CPU周期要贵,因为你的条件在短时间内可以保证满足,那么繁忙的等待可能对你有好处。

否则,你可以通过睡眠或cond_wait() ing强制放弃CPU。

其他情况下,我可以想到强大的上下文切换出如下:

 while(condition) sleep(0); 

首先,你有一个误解:

阻止呼叫不是“忙等待”或“旋转锁定”。 阻止呼叫是可以休眠的 – 这意味着CPU可以在其他任务上工作,不会浪费CPU。

在你的问题上阻止呼叫

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

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

这是一个折衷 – 更快的开发和可维护性? 或可扩展性。

我会努力做到这一点,因为通过其他答案提供了足够的解释,是的,从所有这些答案中学习,我认为应该是一个完整的画面。 —

根据我的权衡应答与系统的吞吐量之间的权衡。

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

  • 在所有的系统响应,和
  • 一个特定的或每个进程的响应

我认为系统的响应能力 ,阻止呼叫是最好的方式。 当它将CPU提供给就绪队列中的其他进程时,阻塞呼叫处于阻塞状态。

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

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

我认为最好的办法是 – 设计一个系统时考虑到每个进程的响应能力,而不影响整体响应能力和吞吐量 – 通过实施一个基于优先级的调度器,考虑优先级反转问题,如果增加复杂性打扰你 :)。

//改编的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; }