2017-04-17 38 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,以便更多的程序能夠以一堆內存開銷爲代價來完成,但大多數程序只需要少量的線程即可運行。