什么是multithreading环境中的繁忙旋转?

什么是multithreading环境中的“忙碌旋转”?

它是如何有用的,它如何在multithreading环境中的java中实现?

它在提高应用程序性能方面有何用处?

其他一些答案错过了忙碌等待的真正问题。

除非你在谈论一个关注节约电力的应用,否则燃烧CPU时间本身并不是一件坏事。 当有一些其他线程或进程可以运行时,它才会变坏。 当其中一个准备运行的线程是忙等待循环等待的线程时,它真的很糟糕。

这才是真正的问题。 在普通操作系统上运行的普通用户模式程序无法控制哪些线程在哪些处理器上运行,正常的操作系统无法区分繁忙等待的线程和正在工作的线程之间的区别,即使操作系统知道线程正忙着等待,它也无法知道线程在等什么。

因此,忙碌的服务员完全有可能等待很多毫秒(几乎是永恒的),等待一个事件,而唯一可以使事件发生的线程就在边线(即在运行队列中)等待轮到使用CPU了。

繁忙的等待通常用于系统,在这些系统中,可以严格控制哪些线程在哪些处理器上运行。 当您知道导致它的线程实际上在不同的处理器上运行时,忙等待可以是等待事件的最有效方式。 当您为操作系统本身编写代码时,或者当您编写在实时操作系统下运行的嵌入式实时应用程序时,通常会出现这种情况。


凯文沃尔特斯写了关于等待时间很短的案例。 可以允许在普通OS上运行的CPU绑定的普通程序在每个时间片中执行数百万条指令。 因此,如果程序使用自旋锁来保护仅由几条指令组成的关键部分,那么任何线程在关键部分中都不会丢失其时间片。 这意味着,如果线程A发现自旋锁被锁定,那么持有锁的线程B很可能实际上在不同的CPU上运行。 这就是为什么当你知道它将在多处理器主机上运行时,在普通程序中使用自旋锁是可以的。

忙等待或旋转是一种技术,其中进程重复检查条件是否为真,而不是调用等待或睡眠方法并释放CPU。

1.它主要用于多核处理器,其中条件将很快变为真,即以毫秒或微秒为单位

2.不释放CPU的优点是,所有缓存的数据和指令都不受影响,如果这个线程暂停在一个核心并带回另一个线程,这可能会丢失

忙碌的旋转是在不释放CPU的情况下等待事件的技术之一。 通常这样做是为了避免丢失CPU缓存中的数据,如果线程暂停并在其他核心中恢复则会丢失。

因此,如果您正在处理订单处理线程当前没有任何订单的低延迟系统,而不是hibernate或调用wait() ,您可以循环然后再次检查队列中的新消息。 如果你需要等待很短的时间,例如微秒或纳秒,这是唯一有益的。

LMAX Disrupter框架是一个高性能的线程间消息传递库,它有一个BusySpinWaitStrategy,它基于这个概念,并为等待屏障的EventProcessors使用繁忙的自旋循环。

“忙碌的旋转”在一个线程中不断循环,以查看另一个线程是否已完成某些工作。 这是一个“坏主意”,因为它只是在等待消耗资源。 最忙碌的旋转甚至没有睡觉,但是尽可能快地旋转等待工作完成。 通过直接完成工作来通知等待线程并让它在此之前让它睡眠不那么浪费。

注意,我称之为“坏主意”,但它在某些情况下用于低级代码以最小化延迟,但这在Java代码中很少(如果有的话)需要。

从性能的角度来看,忙碌的旋转/等待通常是一个坏主意。 在大多数情况下,最好在准备好运行时睡觉并等待信号,而不是旋转。 采取有两个线程的场景,线程1正在等待线程2设置变量(比如,它等到var == true 。然后,它会忙着旋转

 while (var == false) ; 

在这种情况下,您将占用线程2可能正在运行的大量时间,因为当您醒来时,您只是无意识地执行循环。 所以,在你等待这样的事情发生的情况下,最好让线程2通过让自己进入睡眠并让它在完成时唤醒你来完全控制。

但是,在极少数情况下,您需要等待的时间非常短旋转锁定实际上更快 。 这是因为执行signalngfunction所需的时间; 如果使用的旋转时间小于执行信号所需的时间,则旋转是优选的。 因此,以这种方式它可能是有益的,并且实际上可以提高性能,但这绝对不是最常见的情况。

旋转等待是你经常等待条件成真。 相反的是等待信号(如notify()和wait()的线程中断)。

有两种等待方式,第一种是半活动(睡眠/产量)和活动(忙等待)。

在忙碌的等待中,程序主动使用H​​LT或NOP等特殊操作码或其他耗时的操作来闲置。 其他只使用while循环检查条件是否为真。

JavaFramework为线程提供Thread.sleep,Thread.yield和LockSupport.parkXXX()方法来移交cpu。 睡眠等待特定的时间,但即使指定了纳秒,alwasy也会花费一毫秒。 LockSupport.parkNanos(1)也是如此。 Thread.yield允许我的示例系统(win7 + i5 mobile)的分辨率为100ns。

产量问题就是它的工作方式。 如果系统被充分利用,在我的测试场景中,产量最多可能需要800毫秒(100个工作线程都会无数次地计算一个数字(a + = a;))。 由于yield释放了cpu并将线程添加到其优先级组中所有线程的末尾,因此,除非在某个特定范围内未使用cpu,否则yield会不稳定。

繁忙的等待将阻止CPU(核心)持续数毫秒。

Java Framework(检查Condition类实现)使用活动(忙)等待的周期小于1000ns(1微秒)。 在我的系统中,System.nanoTime的平均调用需要160ns,所以忙碌的等待就像检查条件在nanoTime上花费160ns并重复一样。

所以基本上Java(队列等)的并发框架就像在微秒旋转下等待并在N粒度内点击等待的周期,其中N是用于检查时间约束的纳秒数并等待一毫秒或更长(对于我当前的系统)。

因此,积极忙碌的等待会增加利用率,但有助于系统的整体react native。

在烧掉CPU时间时,应该使用特殊指令来降低执行耗时操作的核心的功耗。

繁忙的旋转只不过是循环,直到线程完成。 例如你说了10个线程,你想等待所有线程完成然后想继续,

 while(ALL_THREADS_ARE_NOT_COMPLETE); //Continue with rest of the logic 

例如,在java中,您可以使用ExecutorService管理多个线程

  ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++) { Runnable worker = new WorkerThread('' + i); executor.execute(worker); } executor.shutdown(); //With this loop, you are looping over till threads doesn't finish. while (!executor.isTerminated()); 

这是一个繁忙的旋转,因为它消耗资源,因为CPU不是理想的,但保持在循环上运行。 我们应该有机制来通知主线程(父线程)以指示所有线程都已完成,并且它可以继续执行其余任务。

使用前面的示例,您可以使用不同的机制来提高性能,而不是忙碌旋转。

  ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++) { Runnable worker = new WorkerThread('' + i); executor.execute(worker); } executor.shutdown(); try { executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { log.fatal("Exception ",e); }