Perl 6可以保留多少个承诺?

bri*_*foy 8 multithreading perl6 promise

这是一个有点滑稽的标题,但在玩Promises时,我想看看我能在多大程度上延伸这个想法.在这个程序中,我这样做,所以我可以指定我想要做多少个承诺.

  • 线程调度程序中的默认值是16个线程(rakudo/ThreadPoolScheduler.pm)
  • 如果我指定的数字超过该数字,程序会挂起,但我没有收到警告(比如"太多线程").
  • 如果我设置RAKUDO_MAX_THREADS,我可以停止程序挂起,但最终还是有太多的线程竞争要运行.

我有两个问题,真的.

  • 程序如何知道它可以创建多少个线程?这比承诺的数量略多,因为这是值得的.

  • 我怎么知道我应该允许多少线程,即使我可以赚更多?

这是我小小的Macbook Air上的Rakudo 2017.01,有4个核心:

my $threads = @*ARGS[0] // %*ENV<RAKUDO_MAX_THREADS> // 1;
put "There are $threads threads";

my $channel = Channel.new;

# start some promises
my @promises;
for 1 .. $threads {
    @promises.push: start {
        react {
            whenever $channel -> $i {
                say "Thread {$*THREAD.id} got $i";
                }
            }
        }
    }
put "Done making threads";

for ^100 { $channel.send( $_ ) }
put "Done sending";

$channel.close;

await |@promises;

put "Done!";
Run Code Online (Sandbox Code Playgroud)

Jon*_*ton 11

这实际上不是关于Promise本身,而是关于线程池调度程序.一个Promise本身只是一个同步结构.该start构造实际上做了两件事:

  1. 确保了新鲜$_,$/以及$!该块的内
  2. Promise.start用该块调用

还有Promise.start两件事:

  1. 创建并返回一个 Promise
  2. 安排块中的代码在线程池上运行,并安排成功完成保持Promise和异常中断Promise.

拥有Promise不受线程池上代码支持的对象不仅可能,而且相对常见.Promise.in,Promise.anyofPromise.allof工厂不马上安排任何事情,并且有各种的用途Promise,涉及做Promise.new,然后调用keepbreak以后.所以我可以轻松创建并await在1000 Promise秒:

my @p = Promise.new xx 1000;
start { sleep 1; .keep for @p };
await @p;
say 'done' # completes, no trouble
Run Code Online (Sandbox Code Playgroud)

同样,a Promise不是唯一可以在其上安排代码的东西ThreadPoolScheduler.返回的许多东西Supply(如间隔,文件监视,异步套接字,异步进程)都在那里安排它们的回调.通过执行可以将代码抛到那里,$*SCHEDULER.cue: { ... }即使你知道结果或任何错误,所以它并不是特别常见.

当前的Perl 6线程池调度程序具有可配置但强制执行的上限,默认为16个线程.如果您创建了一个所有16个都被占用但无法取得进展的情况,并且唯一可以取得进展的事情就是卡在工作队列中,那么就会发生死锁.这对于Perl 6线程池来说并不是唯一的.任何有界池都将容易受到攻击(并且任何无界池都将容易耗尽所有资源并使进程死亡:-)).

正如另一篇文章所述,Perl 6.d将制作awaitreact非阻塞结构; 这一直是计划,但没有足够的开发资源及时实现Perl 6.c. 该use v6.d.PREVIEWpragma提供了对此功能的早期访问.(另外,公平警告,这是一项正在进行中的工作.)这样做的结果是线程池拥有的一个awaitreact一个线程将暂停执行预定代码(对于那些好奇的,通过继续)并允许进一步工作的线索.当等待的事情完成或react块得到时,将安排恢复代码done.请注意,这意味着你可以在不同的操作系统线程前后awaitreact在6.d. (大多数Perl 6用户不需要关心这一点.它主要与那些编写C语言绑定或处理系统的东西相关.而且一个好的C库绑定将使得绑定的用户没有照顾.)

即将到来的6.d更改并没有消除耗尽线程池的可能性,但这意味着你可以在6.c中做的一系列方法将不再受到关注(注意,编写递归征服/划分事物)该await分开的部分,或具有数以千计的活性的结果与反应推出块start react { ... }).

展望未来,线程池调度程序本身也将变得更加智能.接下来是猜测,虽然我可能会实施这些变化,但这可能是最好的推测.:-)线程池将在进行进度后开始,并使用它来动态调整池大小.这将包括注意到没有进展,并结合工作队列包含项目的观察,添加线程以尝试解决死锁 - 以增加线程的内存开销为代价.今天线程池保守地倾向于产生最大尺寸,即使这不是一个特别理想的选择; 最有可能使用某种爬山算法来尝试确定最佳数量.一旦发生这种情况,可以大大提高默认的max_threads,这样就可以完成更多的程序 - 以一堆内存开销为代价 - 但大多数程序只运行一些线程.


Bra*_*ert 6

快速修复,添加use v6.d.PREVIEW;第一行.
这解决了许多线程耗尽问题.

我添加了一些其他更改$*SCHEDULER.max_threads,并添加了Promise"id",以便很容易看出Thread ID不一定与给定的Promise相关联.

#! /usr/bin/env perl6

use v6.d.PREVIEW; # <--

my $threads = @*ARGS[0] // $*SCHEDULER.max_threads;
put "There are $threads threads";

my $channel = Channel.new;

# start some promises
my @promises;
for 1 .. $threads {
    @promises.push: start {
        react {
            whenever $channel -> $i {
                say "Thread $*THREAD.id() ($_) got $i";
            }
        }
    }
}
put "Done making threads";

for ^100 { $channel.send( $_ ) }
put "Done sending";

$channel.close;

await @promises;

put "Done!";
Run Code Online (Sandbox Code Playgroud)
There are 16 threads
Done making threads
Thread 4 (14) got 0
Thread 4 (14) got 1
Thread 8 (8) got 3
Thread 10 (6) got 4
Thread 6 (1) got 5
Thread 16 (5) got 2
Thread 3 (16) got 7
Thread 7 (8) got 8
Thread 7 (9) got 9
Thread 5 (3) got 6
Thread 3 (6) got 10
Thread 11 (2) got 11
Thread 14 (5) got 12
Thread 4 (16) got 13
Thread 16 (15) got 14 # <<
Thread 13 (11) got 15
Thread 4 (15) got 16 # <<
Thread 4 (15) got 17 # <<
Thread 4 (15) got 18 # <<
Thread 11 (15) got 19 # <<
Thread 13 (15) got 20 # <<
Thread 3 (15) got 21 # <<
Thread 9 (13) got 22
Thread 18 (15) got 23 # <<
Thread 18 (15) got 24 # <<
Thread 8 (13) got 25
Thread 7 (15) got 26 # <<
Thread 3 (15) got 27 # <<
Thread 7 (15) got 28 # <<
Thread 8 (15) got 29 # <<
Thread 13 (13) got 30
Thread 14 (13) got 31
Thread 8 (13) got 32
Thread 6 (13) got 33
Thread 9 (15) got 34 # <<
Thread 13 (15) got 35 # <<
Thread 9 (15) got 36 # <<
Thread 16 (15) got 37 # <<
Thread 3 (15) got 38 # <<
Thread 18 (13) got 39
Thread 3 (15) got 40 # <<
Thread 7 (14) got 41
Thread 12 (15) got 42 # <<
Thread 15 (15) got 43 # <<
Thread 4 (1) got 44
Thread 11 (1) got 45
Thread 7 (15) got 46 # <<
Thread 8 (15) got 47 # <<
Thread 7 (15) got 48 # <<
Thread 17 (15) got 49 # <<
Thread 10 (10) got 50
Thread 10 (15) got 51 # <<
Thread 11 (14) got 52
Thread 6 (8) got 53
Thread 5 (13) got 54
Thread 11 (15) got 55 # <<
Thread 11 (13) got 56
Thread 3 (13) got 57
Thread 7 (13) got 58
Thread 16 (16) got 59
Thread 5 (15) got 60 # <<
Thread 5 (15) got 61 # <<
Thread 6 (15) got 62 # <<
Thread 5 (15) got 63 # <<
Thread 5 (15) got 64 # <<
Thread 17 (11) got 65
Thread 15 (15) got 66 # <<
Thread 17 (15) got 67 # <<
Thread 11 (13) got 68
Thread 10 (15) got 69 # <<
Thread 3 (15) got 70 # <<
Thread 11 (15) got 71 # <<
Thread 6 (15) got 72 # <<
Thread 16 (13) got 73
Thread 6 (13) got 74
Thread 17 (15) got 75 # <<
Thread 4 (13) got 76
Thread 8 (13) got 77
Thread 12 (15) got 78 # <<
Thread 6 (11) got 79
Thread 3 (15) got 80 # <<
Thread 11 (13) got 81
Thread 7 (13) got 82
Thread 4 (15) got 83 # <<
Thread 7 (15) got 84 # <<
Thread 7 (15) got 85 # <<
Thread 10 (15) got 86 # <<
Thread 7 (15) got 87 # <<
Thread 12 (13) got 88
Thread 3 (13) got 89
Thread 18 (13) got 90
Thread 6 (13) got 91
Thread 18 (13) got 92
Thread 15 (15) got 93 # <<
Thread 16 (15) got 94 # <<
Thread 12 (15) got 95 # <<
Thread 17 (15) got 96 # <<
Thread 11 (13) got 97
Thread 15 (16) got 98
Thread 18 (7) got 99
Done sending
Done!
Run Code Online (Sandbox Code Playgroud)