Das ist ein bisschen glitzernder Titel, aber wenn ich mit Promises herumspielte, wollte ich sehen, wie weit ich mich strecken konnte die Idee. In diesem Programm mache ich es so, dass ich angeben kann, wie viele Versprechungen ich machen möchte.
Ich habe wirklich zwei Fragen.
Wie würde ein Programm wissen, wie viele weitere Threads es machen kann? Das ist etwas mehr als die Anzahl der Versprechen, für das, was das wert ist.
Wie würde ich wissen, wie viele Threads ich erlauben sollte, auch wenn ich mehr machen kann?
Das ist Rakudo 2017.01 auf meinem kleinen Macbook Air mit 4 Kernen:
%Vor% Es handelt sich nicht wirklich um Promise
per se, sondern um den Thread Pool Scheduler. A Promise
selbst ist nur ein Synchronisationskonstrukt. Das Konstrukt start
macht tatsächlich zwei Dinge:
$_
, $/
und $!
innerhalb des Blocks Promise.start
mit diesem Block Und Promise.start
macht auch zwei Dinge:
Promise
zurück
Promise
behält und eine Ausnahme den Promise
. Es ist nicht nur möglich, sondern auch relativ üblich, Promise
-Objekte zu haben, die nicht vom Code im Thread-Pool unterstützt werden. Promise.in
, Promise.anyof
und Promise.allof
Fabriken planen nicht sofort etwas, und es gibt alle Arten von Verwendungen von Promise
, die Promise.new
betreffen und dann später keep
oder break
aufrufen. So kann ich einfach und await
auf 1000 Promise
s:
Gleichermaßen ist ein Promise
nicht die einzige Sache, die Code auf dem ThreadPoolScheduler
einplanen kann. Die vielen Dinge, die Supply
(wie Intervalle, Dateiüberwachung, asynchrone Sockets, asynchrone Prozesse) zurückgeben, planen alle ihre Callbacks auch dort. Es ist möglich, Code in den Stil "Fire-and-Forget" zu schreiben, indem Sie $*SCHEDULER.cue: { ... }
ausführen (obwohl Sie sich oft um das Ergebnis oder irgendwelche Fehler kümmern, daher ist es nicht besonders häufig).
Der aktuelle Perl 6-Thread-Pool-Scheduler verfügt über eine konfigurierbare, aber erzwungene Obergrenze, die standardmäßig auf 16 Threads festgelegt ist. Wenn Sie eine Situation erstellen, in der alle 16 belegt sind, aber keine Fortschritte erzielen können und nur Fortschritte in der Arbeitswarteschlange auftreten, tritt ein Deadlock auf. Dies ist nichts Einzigartiges für Perl 6 Threadpool; Jeder gebundene Pool ist dafür anfällig (und jeder unbeschränkte Pool ist anfällig dafür, alle Ressourcen zu verbrauchen und den Prozess zum Erliegen zu bringen: -)).
Wie in einem anderen Post erwähnt, macht Perl 6.d await
und react
nicht blockierende Konstrukte; Dies war immer der Plan, aber es gab nicht genügend Entwicklungsressourcen, um es rechtzeitig für Perl 6.c. zu realisieren. Das Pragma use v6.d.PREVIEW
bietet einen frühzeitigen Zugriff auf diese Funktion. (Auch eine faire Warnung, es ist ein Work in Progress.) Das Ergebnis davon ist, dass ein await
oder react
auf einem Thread, der dem Thread-Pool gehört, die Ausführung des geplanten Codes (für diejenigen, die neugierig sind) pausiert eine Fortsetzung) und und lassen Sie den Thread mit der weiteren Arbeit weitermachen. Die Wiederaufnahme des Codes wird geplant, wenn die erwartete Sache abgeschlossen ist, oder der react
-Block bekommt done
. Beachten Sie, dass Sie sich vor und nach dem await
oder react
in 6.d.in einem anderen Betriebssystem-Thread befinden können. (Die meisten Perl 6-Benutzer müssen sich nicht darum kümmern. Dies ist hauptsächlich für diejenigen relevant, die Bindungen zu C-Bibliotheken schreiben oder über System-y-Sachen. Und eine gute C-Bibliotheksbindung macht es so, dass Benutzer der Bindung nicht haben zu kümmern.)
Die bevorstehende 6.d-Änderung beseitigt nicht die Möglichkeit, den Thread-Pool zu erschöpfen, aber es wird bedeuten, dass eine Reihe von Möglichkeiten, die Sie in 6.c tun können, nicht mehr von Belang sind (rekursive Eroberung zu schreiben) / Teile Dinge, die await
die Ergebnisse der geteilten Teile, oder Tausende von aktiven reagieren Blöcke gestartet mit start react { ... }
).
Mit Blick auf die Zukunft wird der Thread-Pool-Scheduler selbst intelligenter werden. Was folgt ist Spekulation, obwohl ich wahrscheinlich derjenige sein werde, der die Änderungen implementiert, ist es wahrscheinlich die beste Spekulation im Angebot. :-) Der Thread-Pool beginnt nach dem Fortschritt und verwendet ihn, um die Poolgröße dynamisch anzupassen. Dazu gehört, dass keine Fortschritte gemacht werden, und in Verbindung mit der Beobachtung, dass die Arbeitswarteschlangen Elemente enthalten, Threads, um den Deadlock zu versuchen und zu beheben - auf Kosten des Speicher-Overheads von hinzugefügten Threads. Heute tendiert der Thread-Pool konservativ dazu, bis zu seiner maximalen Größe aufzutauchen, auch wenn dies keine besonders optimale Wahl ist; sehr wahrscheinlich wird eine Art Hill-Climbing-Algorithmus verwendet, um zu versuchen, sich auf eine optimale Zahl zu konzentrieren. Sobald dies geschehen ist, kann der Standardwert von max_threads wesentlich erhöht werden, so dass mehr Programme - auf Kosten von einem Haufen Speicheraufwand - fertiggestellt werden können, aber die meisten werden nur mit einer Handvoll Threads ausgeführt.
Schnellkorrektur, fügen Sie use v6.d.PREVIEW;
in der ersten Zeile hinzu.
Dies behebt eine Reihe von Thread-Erschöpfungsproblemen.
Ich habe ein paar andere Änderungen wie $*SCHEDULER.max_threads
hinzugefügt und die Promise "id" hinzugefügt, so dass es leicht zu sehen ist, dass die Thread-ID nicht unbedingt mit einer gegebenen Promise übereinstimmt.
Tags und Links multithreading perl6 promise