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.)
Antworten:
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.
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.)
quelle
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.
quelle
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.
quelle
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.
quelle
Ä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?
quelle
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.
quelle
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.
quelle
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.
quelle
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.
quelle
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
memset
eine intrinsische oder eine Funktion? Ist die Zuweisung von Ganzzahlen oder die Zuweisung von benutzerdefinierten Typen ein Funktionsaufruf?quelle