C语言 “忙碌等待”与“睡眠”之间的权衡是什么?

um6iljoc  于 2023-01-12  发布在  其他
关注(0)|答案(6)|浏览(146)

这是我上一个问题的延伸
How does blocking mode in unix/linux sockets works?
我现在从互联网上收集到的信息是,所有调用阻塞调用的进程都被置于睡眠状态,直到调度程序找到解除阻塞的原因。原因可以是缓冲区空、缓冲区满或任何其他情况。
但是,这是否是实现实时的有效方法,比如说硬实时应用程序?因为当解阻塞条件为真时,进程不会被解阻塞,而是当调度器给它CPU片,并且解阻塞条件都为真时。
如果你想要一个响应的解决方案,我不认为“旋转锁”或“忙碌等待”是正确的方式,CPU片被浪费,总的来说,系统将变得没有响应或可能响应不良。
有人能帮我理清这些矛盾的想法吗。

44u64gxh

44u64gxh1#

进入睡眠状态直到调度程序唤醒你是正常/首选的事情。
旋转(等待的替代方法,不休眠)不太常见,具有以下效果:

  • 使CPU保持忙碌,并阻止其他线程使用CPU(直到/除非自旋线程完成其时间片并被预占)
  • 可以停止旋转的那一刻,你正在等待的事情发生(因为你不断地检查该事件,你不需要花时间来唤醒,因为你已经醒了)
  • 不调用进入睡眠和再次唤醒所需的CPU指令

如果延迟的长度 * 非常 * 短(例如,如果延迟仅为执行100个CPU指令所需的时间),则旋转可以比进入休眠更高效(更少的总CPU)。

zbsbpyhn

zbsbpyhn2#

旋转锁定会在所需事件未发生时消耗CPU和轮询的资源路径,从而继续浪费资源。
Blocking操作最重要的不同之处在于,不使用CPU和关联的资源路径,而是在预期从中发生所需事件的资源上安装某种形式的wait

在多任务或多线程/处理器环境中(这是很长一段时间以来的常见情况),当所需事件尚未到达时,可能会有其他操作,因此占用CPU和资源访问路径会导致处理能力和时间的严重浪费。
当我们有一个超线程系统时(就像我认为你在你的问题中提到的那样),重要的是要注意CPU线程被划分的粒度是非常高的。我也会冒险观察到所有的事件--你倾向于阻塞的事件--都需要足够的时间才能出现,以补偿它们在解除阻塞之前不得不额外等待的小时间片。
我认为J-16的观点是针对这样一种情况,即一个休眠(阻塞)线程在阻塞状态下让它的代码和数据空间闲置。这可能会使系统放弃资源(如数据/代码缓存),当块被释放时,这些资源需要重新填充。因此,根据条件,块可能会导致更多的资源浪费。
这也是一个有效的注解,应该在设计和实现中检查。
但是,在大多数情况下,阻塞通常比自旋锁更好。

3j86kqsm

3j86kqsm3#

如果在应用程序的用例中,上下文切换比消耗几个CPU周期要昂贵,因为可以保证在短时间内满足您的条件,那么忙碌等待可能对您有好处。
否则,您可以通过休眠或cond_wait() ing强制放弃CPU。
我能想到的另一个强制上下文切换的场景如下:

while(condition)
    sleep(0);
8aqjt8rx

8aqjt8rx4#

首先,你有一个误解

阻塞调用不是“忙碌等待”或“自旋锁”。阻塞调用是可休眠的--这意味着CPU将工作在其他任务上,没有CPU被浪费。

关于你的电话屏蔽问题

阻塞调用更容易--它们更容易理解、更容易开发、更容易调试。
但是它们是资源占用者,如果不使用线程,会阻塞其他客户端;如果使用线程,每个线程都会占用内存和其他系统资源,即使内存足够,切换线程也会使该高速缓存变冷,降低性能。
这是一种权衡--更快的开发和可维护性?或可伸缩性。

6yt4nkrj

6yt4nkrj5#

我会尽量切中要害,因为这里通过其他答案提供了足够的解释,是的,从所有这些答案中学习,我认为应该是一个完整的画面。
我认为应在系统的响应性与吞吐量之间进行权衡。

  • 响应能力 * -可从两个Angular 考虑
  • 所有系统响应性,以及
  • 特定的或每个进程的响应性

我认为对于系统的响应性来说,阻塞调用是最好的方法。因为它把CPU给了准备队列中的其他进程,当阻塞调用处于阻塞状态时。
当然,对于 * 特定进程或每个进程的响应性 *,我们将考虑忙等待/自旋锁模型。
现在,同样为了提高整体 * 系统响应能力 *,我们不能减少时间片(细粒度),因为这会在上下文切换中浪费太多的CPU资源。因此,系统的 * 吞吐量 * 会急剧下降。当然,很明显,阻塞模型增加了系统的吞吐量,因为被阻塞的调用不消耗CPU片并且将其给予就绪队列中的另一/下一进程。
我认为最好的办法是--设计一个系统时考虑到每个进程的响应性,而不影响整体的响应性和吞吐量--通过实现一个基于优先级的调度程序,如果增加复杂性不会困扰您的话,考虑优先级反转问题:)。

ruarlubt

ruarlubt6#

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

相关问题