2017-04-17 68 views
8

这是一个glib标题,但是在玩Promises时,我想看看我可以在多大程度上延伸这个想法。在这个程序中,我是这样做的,所以我可以指定我想要做出多少承诺。Perl 6可以承诺多少个承诺?

  • 在线程调度器的默认值是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!"; 
+1

其中一些可能随下一版本的Perl 6.d:https:// github而改变。com/rakudo/rakudo/pull/1004 –

+1

这不是关于承诺,而是关于线程... –

+2

作为一个消费者,只要我尝试做的事情真正起作用,我不在乎它是否是线程。如果承诺以其他方式实施,我会有同样的问题。 –

回答

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!"; 
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! 
+1

记录Promise“id”在哪里? –

+1

@briandfoy有没有这样的事情,因此引用标记。由于for循环,我指的是'$ _'中的数字。 –

11

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

  1. 确保新鲜$_$/,并且$!块内
  2. 呼叫Promise.start与块

而且Promise.start还做了两两件事:

  1. 创建并返回一个Promise
  2. 计划要在线程池上运行的代码块,并安排成功完成将保留Promise,并且例外会破坏Promise

不仅在线程池中有代码支持的Promise对象不仅可能,而且相对常见。Promise.in,Promise.anyofPromise.allof工厂不会立即安排任何事情,并且Promise有各种用途,包括执行Promise.new,然后稍后调用keepbreak。所以,我可以轻松地创建和await上千家Promise S:

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将使非阻塞构造成为awaitreact;这一直是计划,但是没有足够的开发资源来及时为Perl 6.c实现它。 use v6.d.PREVIEW编译指示提供了对此功能的早期访问。 (此外,公正的警告,这是一项正在进行的工作)。结果是线程池拥有的线程上的awaitreact将暂停计划代码的执行(对于那些好奇的人,通过继续)以及并让线程继续进行下一步工作。代码的恢复将在等待的事情完成时安排,或react区块得到done。请注意,这意味着您可以在6.d中的awaitreact之前和之后位于不同的OS线程中。 (大多数Perl 6用户不需要关心这个问题,它主要与那些编写与C库绑定或者处理system-y的东西有关,而且一个好的C库绑定会使它绑定的用户不具有)

即将推出的6.d更改并不能消除线程池耗尽的可能性,但它意味着你可以在6.c中执行的一系列方法将不再受到关注(请注意,编写递归征服/划分零件的结果,或者使用start react { ... }启动数千个有效反应块。

展望未来,线程池调度器本身也将变得更加智能。接下来是猜测,但考虑到我可能是实施变化的人,它可能是最好的推测。 :-)线程池将在正在进行的进程之后启动,并使用它来动态调整池大小。这将包括注意到没有取得进展,并且结合观察到工作队列包含项目,添加线程来尝试和解决死锁 - 这是以增加线程的内存开销为代价的。今天,线程池保守地倾向于产生到其最大尺寸,即使这不是特别优选的选择;最有可能的是某种爬山算法将被用来尝试和解决一个最优数量。一旦发生这种情况,可以大幅提高默认的max_threads,以便更多的程序能够以一堆内存开销为代价来完成,但大多数程序只需要少量的线程即可运行。