Programmiersprache, in der jeder Funktionsaufruf / Block in einem separaten Thread ausgeführt wird? [geschlossen]

26

Ich erstelle gerade zum Spaß eine Programmiersprache, in der jeder Funktionsaufruf / neuer Block (if-Klauseln, Schleifen usw.) in einem separaten Thread ausgeführt wird. Anstatt neue Threads zu erstellen, sollte der Standard sein, dass dies automatisch erfolgt, und wenn Sie möchten, dass es im Haupt-Thread ausgeführt wird, müssen Sie dies angeben.

Ich bin nicht so gut informiert über Multithread-Programmierung, aber ich kenne die Grundlagen (Futures, thread-sichere Objekte). Daher frage ich mich, wie eine solche Sprache syntaktisch aussehen könnte und ob es überhaupt möglich ist, damit anzufangen. Das Ziel ist nicht, es "nützlich" zu machen, es ist eher zum Spaß und zum Lernen.

(Es tut mir leid, wenn dies der falsche Ort zum Posten ist. Wenn ja, würde ich mich freuen, wenn Sie mich auf den richtigen Ort verweisen, an dem eine Frage wie meine zulässig ist.)

Grimbox
quelle
17
Das Erstellen von Threads ist einfach. Der Trick beim Multithreading ist, wenn sie miteinander sprechen oder mit denselben Ressourcen arbeiten müssen. Mit anderen Worten, es ist nicht die Gabelung, die hart ist, es sind die Verbindungen. Das Hauptproblem, das Sie lösen müssen, ist, wie Sie das kontrollieren.
JimmyJames
20
IMO, Sie können das nicht tun, ohne die übliche Definition des Wortes "Funktion" oder des Wortes "Thread" ernsthaft zu strecken. Vielleicht möchten Sie etwas über Schauspieler lesen, wenn Sie mit ihnen noch nicht vertraut sind: en.wikipedia.org/wiki/Actor_model
Solomon Slow
9
Wenn Sie feinkörnig genug vorgehen, werden Sie feststellen, dass CPUs bereits Anweisungen automatisch parallelisieren, ohne den Softwareentwickler zusätzlich zu belasten. Siehe Out-of-Order-Ausführung und Superscalar-Prozessor .
8bittree
8
Das klingt eigentlich irgendwie schrecklich. Ich wollte erklären, warum, aber Karl Bielefeldt tat es bereits.
Mason Wheeler
1
Welches Paradigma möchten Sie in Ihrer Sprache unterstützen? Sollte es zwingend oder funktionsfähig sein? Wollen Sie damit sagen, dass alle Anweisungen gleichzeitig ausgeführt werden - die then / else-Blöcke zusammen mit dem, was nach ihnen kommt?
Bergi

Antworten:

39

Jeder Funktionsaufruf / neuer Block (if-Klauseln, Schleifen usw.) wird in einem separaten Thread ausgeführt.

Lesen Sie viel mehr über Fortsetzungen und den Fortsetzungsstil (und ihre Beziehung zu Fäden oder Koroutinen). Ich empfehle, SICP & Lisp In Small Pieces zu lesen . Außerdem bietet die Programmiersprache Pragmatik einen nützlichen Überblick über mehrere Sprachen und würde Ihnen helfen, Ihre eigenen zu entwerfen.

Deshalb frage ich mich, wie eine solche Sprache syntaktisch aussehen könnte

Syntax spielt für das Erkunden von Ideen keine große Rolle . Die Semantik ist viel wichtiger. Ich schlage vor, zunächst eine S-expr- ähnliche Syntax zu verwenden (damit Sie mit Scheme und seinem Aufruf / cc einen Prototyp erstellen können ).

Sobald Ihre Ideen klarer sind (nach einigem Experimentieren), können Sie sie auf Lambda-the-Ultimate diskutieren , eine sexyere Syntax definieren (was tatsächlich wichtig ist, wenn die Leute Ihre Sprache annehmen sollen, und worauf es auch ankommt, ist die Qualität der Implementierung, einer freien Software-Beispielimplementierung, der Dokumentation, der Standardbibliothek, der Fremdfunktionsschnittstelle usw.)

Basile Starynkevitch
quelle
25
+1 für die Angabe, warum die Syntax irrelevant ist. Ein weiteres +1, wenn ich erklären könnte, warum Syntax sehr wichtig ist.
Jörg W Mittag
Guter Vorschlag, aber um Dinge auf LtU zu besprechen, möchten Sie einen Account und es ist nicht trivial, einen aus meiner Erfahrung zu bekommen.
Mael
1
Sie sollten einen Thread-Pool verwenden, um den Overhead zu reduzieren. en.wikipedia.org/wiki/Thread_pool
shawnhcorey
37

Vielleicht möchten Sie etwas über die Erforschung von Daten parallel zu Haskell lesen . Wenn Sie sich auf YouTube umschauen, hat Simon Peyton Jones auch einige interessante Vorträge zum Thema gehalten.

Wenn ich mich richtig an seine Vorträge erinnere, ist es in der reinen funktionalen Programmierung fast trivial, Möglichkeiten zu finden, um Threads zu erstellen. Sein Hauptproblem in seiner Forschung besteht darin, dass zu viele zu kurzlebig sind, sodass der Aufwand für das Erstellen von Threads und das Kommunizieren ihrer Ergebnisse die Vorteile der Parallelität im Wesentlichen überwiegt. Zum Beispiel ist es einfach, einem Compiler beizubringen, 100 Threads für die Berechnung anzulegen sum $ fmap (+1) <100 length vector>, aber lohnt sich der Aufwand?

Der Trick besteht dann darin, Threads zu nützlichen Größen zusammenzufassen, ohne dass der Programmierer damit belastet wird, dies manuell zu bezeichnen. Es ist ein schwieriges Problem, das geknackt werden muss, um zukünftige PCs mit Tausenden von Kernen effektiv zu nutzen.

Karl Bielefeldt
quelle
8
Ich würde schon gerne mit einem altmodischen PC mit nur Hunderten von Kernen arbeiten ...
Hagen von Eitzen
8
Ich möchte darauf hinweisen, dass wir für stark datenparallele Designs bereits viele Parallelisierungen sehr effektiv durchgeführt haben: GPUs bestehen im Wesentlichen aus 200 bis 1000 Kerncomputern (sie haben sogar eine eigene Speicherhierarchie).
Delioth
4
@HagenvonEitzen: Die Azul Vega JCA (Java Compute Appliance) verfügte über 16 CPUs mit jeweils 54 Kernen für insgesamt 864 Kerne und war keine Maschine für Forschungszwecke oder Prototypen, sondern ein echtes Produkt. Es füllte auch nicht ein ganzes Gebäude oder gar einen ganzen Raum, es war ein Gerät in Schreibtischgröße. Und es war vor 9 Jahren verfügbar. GPUs wurden bereits erwähnt, dies war jedoch eine Maschine mit 864 Allzweck- CPU-Kernen.
Jörg W Mittag
3
Natürlich gibt es einen Grund , warum wir tun , jene massiven Daten parallel GPU heute haben, aber nicht mehr jene massiven Multi-Core - CPU für Desktops ist. Die ersteren sind kommerziell rentabel, die letzteren nicht, und das liegt daran, dass Sie sie nicht effizient programmieren konnten.
MSalters
@MSalters vielleicht brauchen wir nur unendlich viele Affen? Oder Quantum-Programme, die bei jedem Schritt einfach alle möglichen Vorgänge ausführen?
15

Genau das macht Erlang. Das erneute Verknüpfen der Threads erfolgt hauptsächlich mithilfe von Warteschlangen. Es ist ein geniales Konzept, aber es ist ein bisschen schwierig, sich anfangs einen Überblick zu verschaffen, wenn Ihr Hintergrund mehr prozedurale Sprachen sind. Ich kann es nur wärmstens empfehlen.

GenericJam
quelle
4

Zuerst würde ich empfehlen, dass Sie sich PROMELA ansehen , eine Sprache, die zur Beschreibung eines gleichzeitigen Algorithmus verwendet wird, damit ein Modellprüfer alle möglichen Ausführungen brutal erzwingen kann, um zu überprüfen, ob es nicht zu falschem Verhalten fähig ist. (Die gleichzeitige Programmierung ist bekanntermaßen schwierig zu finden, weshalb solche Verifikationstechniken wichtig sind.) Es werden nicht alle Konstrukte in separaten Threads ausgeführt, aber die Syntax und Semantik sind eher merkwürdig, da der Schwerpunkt auf dem Nichtdeterminismus von gleichzeitigen Programmen liegt.

Abstrakter betrachtet ist der π-Kalkül ein schöner Ansatz zur Modellierung paralleler Berechnungen. Es ist schwer, sich zurechtzufinden, wenn Sie nicht das Buch Communicating and Mobile Systems: The Pi Calculus von Robin Milner erhalten. Es hat mir geholfen, über parallele Berechnungen im weiteren Sinne nachzudenken, wie "mehrere Threads auf einen gemeinsamen Speicher zugreifen". Es ist ziemlich interessant, wie bedingte Anweisungen, "gotos" usw. aus einfacheren, natürlich parallelen Primitiven aufgebaut werden können.

In Bezug auf die Syntax ... der beste Weg, dies herauszufinden, besteht darin, einige Beispielprogramme zu schreiben. Schreiben Sie ein Programm, um ein Array zu sortieren, oder senden Sie einen Ping-Befehl an mehrere Server und geben Sie an, welcher Server am schnellsten antwortet, oder versuchen Sie, ein Labyrinth parallel zu lösen, oder so weiter. Während Sie dies tun, werden die Dinge, die in Ihrer Syntax fehlen, offensichtlich und Sie können sie hinzufügen. Nachdem Sie mehrere Dinge hinzugefügt haben, fragen Sie sich, ob sie etwas gemeinsam haben. In diesem Fall können Sie möglicherweise einen einfacheren Ansatz finden, der mehreren Zwecken dienen kann.

Artelius
quelle
4

Ähnliche Projekte wurden in der Vergangenheit versucht. Ich schlage vor, die Klassiker zu lesen, um Ideen zu plündern. (Alle Links gehen auf Wikipedia)

  • Unity In dieser Sprache wurde / wird Parallelprogrammierung unterrichtet. Ich glaube nicht, dass es tatsächlich umgesetzt wurde. Syntax ist etwas kryptisch, aber im Grunde haben Sie eine Sammlung von Anweisungen, die in unbekannter Reihenfolge und wiederholt ausgeführt werden, bis nichts mehr zu tun ist. Dies kommt dem, wonach Sie fragen, am nächsten.

  • Occam Diese Sprache wurde für den tatsächlichen Gebrauch entwickelt, hat sich aber nie wirklich durchgesetzt. Hier gibt es ein Schlüsselwort PAR, was bedeutet, dass eine Liste von Anweisungen parallel ausgeführt werden sollte.

  • Erlang Eine andere reale Sprache. Dieser wird von der Telekommunikationsfirma Ericsson verwendet und hat eine ganze Reihe von Anhängern. Sie haben viel Arbeit investiert, um die Parallelität praktisch und nutzbar zu machen.

  • Google GO Dies ist mein Favorit. Konzeptionell ähnlich wie Erlang, jedoch mit besserer Syntax und dem Gewicht von Google dahinter. Was könnte eventuell schief gehen?

Ich möchte mit einer Warnung schließen: Parallelität ist sehr schwer zu korrigieren. Die meisten Fehler in modernen Programmen ist das Ergebnis es immer falsch . Bist du sicher, dass du dorthin willst?

Stig Hemmer
quelle
Kein Verstoß gegen eine Sprache, die nicht auf meiner Liste steht. Es ist nur so, dass mein Wissen begrenzt ist.
Stig Hemmer
3

Es ist möglich, aber es wäre nicht nützlich für 99% aller denkbaren Anwendungen. Logik ist typischerweise sequenzgebunden, es ist ein Fluss. Schritt für Schritt gelangen Sie zu einer Lösung für ein Problem, und die Reihenfolge der Schritte spielt eine Rolle, vor allem, weil die Ausgabe eines Schritts für den nächsten eingegeben wird.

In den wenigen Fällen, in denen viele Aufgaben unabhängig voneinander ausgeführt werden können, ist es in der Regel günstig, sie nacheinander einzurichten, bevor sie parallel ausgeführt werden.

Ich denke also, Sie sollten besser lernen, wie Sie die Multithreading-Funktionen in Ihrer bevorzugten Programmiersprache verwenden.

Martin Maat
quelle
5
Dies ist eine hervorragende Antwort auf eine andere Frage. OP fragt nicht nach der Weisheit eines solchen Schemas, sondern nur, ob es getan werden kann und wie die "Syntax" aussehen würde.
DepressedDaniel
9
Diejenigen, die nicht danach fragen, brauchen am meisten meine Weisheit. Wenn jemand fragt, ob es möglich ist, einen Hügel in einem Einkaufswagen hinunterzufahren, wäre die beste Antwort nicht "Wetten Sie! Schmieren Sie die Räder unbedingt ein!". Es wäre eher wie "Nun ja, aber ...".
Martin Maat
Erinnert mich an die alte Anweisung der EIAO-Assemblersprache - In beliebiger Reihenfolge ausführen . (Und wo ist dieser Schlüssel überhaupt?)
@MartinMaat niemand wird sich umbringen, indem er zum Spaß eine Programmiersprache schreibt
user253751
2

Clojure kann einen Blick für einige Ideen wert sein.

http://clojure-doc.org/articles/language/concurrency_and_parallelism.html

Hier einige Gedanken: Wenn wir eine Recheneinheit nennen, die unabhängig von einander ausgeführt werden kann, dann eine Aufgabe: 1. Aufgaben sind unabhängig, können also gleichzeitig ausgeführt werden. 2. Verschiedene Aufgaben erfordern unterschiedliche Ressourcen und erfordern unterschiedliche Ausführungszeiten. 3. Daher sollten Aufgaben geplant werden für maximalen Durchsatz 4. Das einzige Programm, das als Scheduler fungieren kann, ist das Betriebssystem

Dinge wie der zentrale Versand von Äpfeln sind ein Versuch, einen solchen Planer bereitzustellen.

Das oben Gesagte bedeutet, dass die Verantwortung für die Ausführung von Aufgaben nicht unbedingt bei der Programmiersprache liegt.

Ein zweiter Gedanke ist, die Belastung durch die Programmierung paralleler Systeme so gering wie möglich zu halten. Die einzige Möglichkeit, dies zu tun, besteht darin, jegliche Angabe darüber, wie etwas zu tun ist, aus dem Programm zu entfernen. Ein Programm sollte nur angeben, was getan werden soll, und der Rest sollte automatisch geschehen.

Das oben Gesagte bedeutet wahrscheinlich, dass dynamische Sprachen und eine pünktliche Kompilierung der richtige Weg sind.

foobar
quelle
2

Was Sie suchen, heißt impliziter Parallellismus, und es gibt Sprachen, die dieses Konzept untersucht haben, wie z. B. Sun / Oracle's Fortress . Unter anderem werden (möglicherweise) Schleifen parallel ausgeführt.

Leider wurde es eingestellt und es gibt viele tote Links da draußen, aber Sie können immer noch ein paar PDFs finden, wenn Sie hart genug googeln:

https://www.eecis.udel.edu/~cavazos/cisc879-spring2008/papers/fortress.pdf (die Sprachspezifikation)

http://stephane.ducasse.free.fr/Teaching/CoursAnnecy/0506-Master/ForPresentations/Fortress-PLDITutorialSlides9Jun2006.pdf

http://www.oracle.com/technetwork/systems/ts-5206-159453.pdf

http://dl.acm.org/citation.cfm?id=1122972 (paywalled)

Erwähnenswert ist, dass Sie in der Regel nicht für jede Anweisung / jeden Ausdruck einen eigentlichen Thread starten möchten, da das Erstellen und Starten von Threads in der Regel teuer ist. Stattdessen verfügen Sie über einen Pool von Threads, in dem Sie Aufgaben veröffentlichen, die erledigt werden müssen . Aber das ist ein Implementierungsdetail.

Gustafc
quelle
1
+1 für Mantioning Fortress ... Die Idee der Sprache hat mir sehr gut gefallen. Ich war wirklich traurig, als sie bekannt
gaben
2

Obwohl es sich nicht um eine Programmiersprache als solche handelt, sollten Sie sich VHDL ansehen . Es wird verwendet, um digitale Schaltkreise zu beschreiben, die natürlich alles parallel ausführen, es sei denn, Sie weisen ausdrücklich an, dies seriell zu tun. Es könnte Ihnen einige Ideen geben, wie Sie Ihre Sprache gestalten und für welche Art von Logik es geeignet sein könnte.

OnePie
quelle
0

Dies kann in C ++ recht einfach simuliert werden. Stellen Sie einfach sicher, dass "jeder" * Funktionsaufruf von a implementiert wird std::future. Die Behandlung des Rückgabewerts erfolgt einfach durch Aufrufen .get()der Zukunft.

Daher könnten Sie Ihre Sprache prototypisieren, indem Sie sie in C ++ kompilieren. Dies sagt uns auch, wie die Syntax aussehen würde: Der Hauptunterschied besteht darin, dass Sie den Aufrufpunkt (an dem Eingabeargumente bereitgestellt werden) vom Rückgabepunkt (an dem die Funktionsausgabe verwendet wird) trennen.

(*) Ich sage "jede Funktion", aber es liegt an Ihnen, was als Funktion zählt. Ist memseteine intrinsische oder eine Funktion? Ist die Zuweisung von Ganzzahlen oder die Zuweisung von benutzerdefinierten Typen ein Funktionsaufruf?

MSalters
quelle
Und wenn Sie die Beantwortung einer Frage lange genug aufschieben, ist in der Regel keine Antwort mehr erforderlich. Ich denke, das nennt man "Lazy Existing".