这是一个有点滑稽的标题,但在玩Promises时,我想知道我可以延伸这个想法。在这个程序中,我这样做,所以我可以指定我想要做多少个承诺。
我真的有两个问题。
程序如何知道它可以生成多少个线程?这比承诺的数量略多,因为这是值得的。
我怎么知道我应该允许多少线程,即使我可以赚更多?
这是我小小的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!";
答案 0 :(得分:11)
这实际上并不是Promise
本身,而是关于线程池调度程序。 Promise
本身只是一个同步构造。 start
构造实际上做了两件事:
$_
,$/
和$!
Promise.start
Promise.start
也做了两件事:
Promise
Promise
并且异常中断Promise
。不仅可能,而且相对常见的是,Promise
对象不受线程池上的代码支持。 Promise.in
,Promise.anyof
和Promise.allof
工厂没有立即安排任何事情,Promise
的各种用途涉及做Promise.new
然后调用稍后keep
或break
。因此,我可以轻松地在1000 await
s:
Promise
my @p = Promise.new xx 1000;
start { sleep 1; .keep for @p };
await @p;
say 'done' # completes, no trouble
同样,Promise
并不是唯一可以在ThreadPoolScheduler
上安排代码的东西。返回Supply
的许多内容(如间隔,文件监视,异步套接字,异步进程)都会在那里安排回调。通过执行$*SCHEDULER.cue: { ... }
可以将代码抛到一种即发即弃的风格(虽然通常你关心结果或任何错误,所以它并不是特别常见)。
当前的Perl 6线程池调度程序具有可配置但强制执行的上限,默认为16个线程。如果您创建了一个所有16个都被占用但无法取得进展的情况,并且唯一可以取得进展的事情就是卡在工作队列中,那么就会发生死锁。这对于Perl 6线程池来说并不是唯一的。任何有界池都容易受到攻击(并且任何无界的池都很容易耗尽所有资源并使进程死亡: - ))。
如另一篇文章中所述,Perl 6.d将使await
和react
非阻塞构造;这一直是计划,但没有足够的开发资源及时实现Perl 6.c. use v6.d.PREVIEW
pragma提供对此功能的早期访问权限。 (另外,公平警告,这是一项正在进行的工作。)结果是,线程池拥有的线程上的await
或react
将暂停执行预定代码(对于那些好奇,通过继续)并允许线程继续进行进一步的工作。当等待的事情完成时,或者react
块获得done
时,将安排恢复代码。请注意,这意味着您可以在6.d中的await
或react
之前和之后使用不同的OS线程。 (大多数Perl 6用户不需要关心这一点。它主要与那些编写C语言绑定或处理系统的东西相关。而且一个好的C库绑定将使得绑定的用户没有照顾。)
即将到来的6.d更改并没有消除耗尽线程池的可能性,但这意味着你可以在6.c中做的一系列方法将不再受到关注(注意,编写递归征服) /划分await
分割部分的结果,或者用start react { ... }
启动了数千个活动反应块的东西。
展望未来,线程池调度程序本身也将变得更加智能。接下来是猜测,虽然我可能会实施这些变化,但这可能是最好的推测。 :-)线程池将在进行进度后开始,并使用它来动态调整池大小。这将包括注意到没有进展,并结合工作队列包含项目的观察,添加线程以尝试解决死锁 - 以增加线程的内存开销为代价。今天线程池保守地倾向于产生最大尺寸,即使这不是一个特别理想的选择;最有可能使用某种爬山算法来尝试确定最佳数量。一旦发生这种情况,可以大大提高默认的max_threads,这样就可以完成更多的程序 - 以一堆内存开销为代价 - 但大多数程序只运行一些线程。
答案 1 :(得分:6)
快速修复,在第一行添加function buildGraphNode(treeNode){
var memb;
var child;
if(!(treeNode.data instanceof Array)) {
memb = member("" + treeNode.label, "" + treeNode.icon, '', '#d0000c', '', 'B','\uf16b' );
members.push(memb);
for (var i = 0; i < treeNode.children.length; i++) {
child = buildGraphNode(treeNode.children[i]);
if (child != null && memb != null) {
connections.push(link(memb, child));
}
}
return memb;
}
else{
for (var i = 0; i < treeNode.children.length; i++) {
child = buildGraphNode(treeNode.children[i]);
}
}
}
这解决了许多线程耗尽问题。
我添加了一些其他更改,例如use v6.d.PREVIEW;
,并添加了Promise“id”,以便很容易看到Thread ID不一定与给定的Promise相关。
$*SCHEDULER.max_threads
#! /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!";