繁体   English   中英

在 Loom 中,我可以为 Recursive[Action/Task] 使用虚拟线程吗?

[英]In Loom, can I use virtual threads for Recursive[Action/Task]?

例如,是否有可能将 RecursiveAction 与——而不是 fork/join 池——虚拟线程池结合使用(在我尝试设计不佳的自定义工作之前)?

RecursiveActionForkJoinTask的子类,顾名思义,文档甚至字面意思是,一个

抽象基础 class 用于在ForkJoinPool中运行的任务。

虽然可以使用线程工厂自定义ForkJoinPool ,但它不是标准的线程工厂,而是用于生产ForkJoinWorkerThread实例的特殊工厂 由于这些线程是Thread的子类,因此无法使用虚拟线程工厂创建它们。

因此,您不能将RecursiveAction与虚拟线程一起使用。 这同样适用于RecursiveTask 但是值得重新考虑将这些类与虚拟线程一起使用会给您带来什么。

无论如何,将任务分解为子任务的主要挑战在于您。 这些类为您提供的是专门用于处理 Fork/Join 池和平衡工作负载与可用平台线程的功能。 当你想在它自己的虚拟线程上执行每个子任务时,你不需要这个。 因此,您可以在没有内置类的情况下使用虚拟线程轻松实现递归任务,例如

record PseudoTask(int from, int to) {
    public static CompletableFuture<Void> run(int from, int to) {
        return CompletableFuture.runAsync(
            new PseudoTask(from, to)::compute, Thread::startVirtualThread);
    }

    protected void compute() {
        int mid = (from + to) >>> 1;
        if(mid == from) {
            // simulate actual processing with potentially blocking operations
            LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500));
        }
        else {
            CompletableFuture<Void> sub1 = run(from, mid), sub2 = run(mid, to);
            sub1.join();
            sub2.join();
        }
    }
}

这个例子既不关心限制细分也不关心阻塞join()调用,它在运行时仍然表现良好,例如PseudoTask.run(0, 1_000).join(); 您可能会注意到,对于更大的范围,从其他递归任务实现中获知的技术在这里也很有用,其中子任务相当便宜。

例如,您可能只将范围的一半提交给另一个线程并在本地处理另一半,例如

record PseudoTask(int from, int to) {
    public static CompletableFuture<Void> run(int from, int to) {
        return CompletableFuture.runAsync(
            new PseudoTask(from, to)::compute, Thread::startVirtualThread);
    }

    protected void compute() {
        CompletableFuture<Void> f = null;
        for(int from = this.from, mid; ; from = mid) {
            mid = (from + to) >>> 1;
            if (mid == from) {
                // simulate actual processing with potentially blocking operations
                LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500));
                break;
            } else {
                CompletableFuture<Void> sub1 = run(from, mid);
                if(f == null) f = sub1; else f = CompletableFuture.allOf(f, sub1);
            }
        }
        if(f != null) f.join();
    }
}

这在运行时会产生显着差异,例如PseudoTask.run(0, 1_000_000).join(); 在第二个示例中将仅使用 100 万个线程,而不是 200 万个。 但是,当然,这是与平台线程不同级别的讨论,在平台线程中,这两种方法都无法合理地工作。


另一个即将推出的选项是StructuredTaskScope ,它允许生成子任务并等待它们完成

record PseudoTask(int from, int to) {
    public static void run(int from, int to) {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            new PseudoTask(from, to).compute(scope);
            scope.join();
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
    }

    protected Void compute(StructuredTaskScope<Object> scope) {
        for(int from = this.from, mid; ; from = mid) {
            mid = (from + to) >>> 1;
            if (mid == from) {
                // simulate actual processing with potentially blocking operations
                LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500));
                break;
            } else {
                var sub = new PseudoTask(from, mid);
                scope.fork(() -> sub.compute(scope));
            }
        }
        return null;
    }
}

在这里,任务不等待其子任务完成,而只有根任务等待所有任务完成。 但此功能在孵化器 state 中,因此可能需要比虚拟线程功能更长的时间才能投入生产。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM