work-stealing算法简介 crossbeam-deque包提供了一个无锁的双向队列(deque)。那么这个双向队列在并发中又起到了什么重要的作用呢? 这就涉及到了在多任务环境下的一个重要算法:work-stealing算法,既工作窃取算法。 最初,工作窃取算法是在join/fork的模型下,作为调度算法用来给多线程计算分配任务的。 Dynamic circular work-stealing deque.
work-stealing算法简介 crossbeam-deque包提供了一个无锁的双向队列(deque)。那么这个双向队列在并发中又起到了什么重要的作用呢? 这就涉及到了在多任务环境下的一个重要算法:work-stealing算法,既工作窃取算法。 最初,工作窃取算法是在join/fork的模型下,作为调度算法用来给多线程计算分配任务的。 Dynamic circular work-stealing deque.
工作窃取(Work-Stealing) 在ForkJoin的框架中,很多时候子任务的执行时间是不均匀的,有些子任务的时间比较长,有些子任务执行的时间比较短,子任务时间比较短的在任务完成后,就会去窃取其他未完成的任务执行 Work-Stealing Work-Stealing 的适用场景是不同的任务的耗时相差比较大,即某些任务需要运行较长时间,而某些任务会很快的运行完成,这种情况下用 Work-Stealing 很合适; 但是如果任务的耗时很平均,则此时 Work-Stealing 并不适合,因为窃取任务时不同线程需要抢占锁,这可能会造成额外的时间消耗,而且每个线程维护双端队列也会造成更大的内存消耗。
这样的结构可以方便的进行工作窃取(work-stealing)。 什么是work-stealing呢? 默认情况下,work thread从分配给自己的那个队列头中取出任务。 pool; // the pool this thread works in final ForkJoinPool.WorkQueue workQueue; // work-stealing 另外一个是支持 work-stealing机制的Queue。
这样的结构可以方便的进行工作窃取(work-stealing)。什么是work-stealing呢?默认情况下,work thread从分配给自己的那个队列头中取出任务。 pool; // the pool this thread works in final ForkJoinPool.WorkQueue workQueue; // work-stealing 另外一个是支持 work-stealing机制的Queue。
Java8 中,默认创建线程池的方法多了一个——Executors.newWorkStealingPool(),newWorkStealingPool 的文档描述: “Creates a work-stealing /** * Creates a work-stealing thread pool using all * {@link Runtime#availableProcessors available processors
A ForkJoinPool differs from other kinds of ExecutorService mainly by virtue of employing work-stealing 工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。工作窃取的运行流程图如下: ?
A work-stealing pool makes no * guarantees about the order in which submitted tasks are * executed ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); } /** * Creates a work-stealing
ForkJoinPool采用分治+work-stealing的思想。可以让我们很方便地将一个大任务拆散成小任务,并行地执行,提高CPU的使用率。 } } } return count; } } 原理说明 所谓work-stealing 因此,在这样的情况下,ForkJoinPool的work-stealing的方式就体现出了优势。每个任务分配的子任务也由自己执行,只有自己的任务执行完成时,才会去执行别的工作线程的任务。
The fork/join framework is distinct because it uses a work-stealing algorithm. ForkJoinPool implements the core work-stealing algorithm and can execute ForkJoinTasks.
subcrate组织): 有锁并发、无锁并发和crossbeam简介 crossbeam-epoch:基于epoch的无锁垃圾收集,以及reiber_stack的例子 crossbeam-deque:work-stealing
并且还实现了 ForkJoinPool 的核心思想 work-stealing。 private fun trySteal(blockingOnly: Boolean): Task? { ... } work-stealing的核心逻辑是,所有线程先去执行CPU密集型的coroutine。CPU密集型队列执行完之后,线程再去执行IO密集型coroutine。 这是整个协程调度里最精彩的部分,work-stealing的设计,加上把CPU-bounded和IO-intensive任务区分出来,使得用了协程的代码效率得到极大的提升。
Work-stealing 窃取算法 Work-stealing 算法是一种用于实现并行计算和负载平衡的策略。它通常用于在分布式系统和多核处理器中,以高效地分配和平衡计算任务。 Work-stealing 算法的优点是它可以实现高效的负载平衡和并行计算,同时减少了任务的等待时间。 Work-stealing 窃取算法通过允许空闲线程从繁忙线程窃取任务来优化资源利用。 任务存储在双端队列中,存储采用后进先出策略,窃取采用先进先出策略。
go语言的内置了自己的调度器,而Quasar则是默认使用ForkJoinPool这个具有work-stealing功能的线程池来当调度器。 work-stealing非常重要,因为你不清楚哪个Fiber会先执行完,而work-stealing可以动态的从其他的等等队列偷一个context过来,这样可以最大化使用CPU资源。
ThreadPoolExecutor:标准线程池 ScheduledThreadPoolExecutor:支持延迟任务的线程池 ForkJoinPool:类似于ThreadPoolExecutor,但是使用work-stealing 模式,其会为线程池中的每个线程创建一个队列,从而用work-stealing(任务窃取)算法使得线程可以从其它线程队列里窃取任务来执行。
这里我把ForkJoin称为刀叉框架,刀叉框架和Executor框架不一样的地方在于work-stealing算法,不同于Executor框架,刀叉框架在等待子任务的完成之前就已经创建并开始运行Join 刀叉框架的核心是两个类: (1)ForkJoinPool,它实现了ExecutorService接口和work-stealing算法,通过它可以很好的管理正在运行的任务以及了解任务的信息。
作者认为选择工作窃取(work-stealing) 还是无共享(share-nothing)取决于应用程序的具体需求。
下面我想通过三个案例来分享下如何利用线程池、任务窃取(Work-Stealing)和负载均衡策略来优化并发性能。 任务窃取(Work-Stealing)场景在任务执行时间不均衡的场景下,使用 ForkJoinPool 的任务窃取机制可以动态分配任务,避免线程空闲。
/Join原理-工作窃取算法 Fork/Join最核心的地方就是利用了现代硬件设备多核,在一个操作时候会有空闲的cpu,那么如何利用好这个空闲的cpu就成了提高性能的关键,而这里我们要提到的工作窃取(work-stealing Fork/Join工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。 那么为什么需要使用工作窃取算法呢?
它基于工作窃取(work-stealing)算法实现,能够高效地利用多核处理器资源。