HelolW rdlo (Eine Threading-Herausforderung)


39

Ich habe eine Herausforderung für dich:

  • Drucken Sie "Hello World" in einer beliebigen Sprache.
  • Jedes Zeichen muss aus einem eigenen, eindeutigen Thread gedruckt werden

Das ist es. Da es keine Garantie dafür gibt, dass die Threads in der Reihenfolge ausgeführt werden, in der Sie sie starten, müssen Sie Ihren Programm-Thread sicher machen, um sicherzustellen, dass die Ausgabe in der richtigen Reihenfolge gedruckt wird.

Und weil dies Codegolf ist, gewinnt das kürzeste Programm.

Aktualisieren:

Der Gewinner ist Marinus 'APL-Eintrag mit 34 Zeichen. Es gewinnt auch den Preis für den am wenigsten lesbaren Eintrag.


10
Ein besserer Name dafür wäre gewesenHelolW rdlo
Cristian Lupascu

Ha, das gefällt mir. Ändern Sie es sofort: D
Tharwen

Aww ... es ist zu kurz
Tharwen

1
Es ist lustig zu sehen, wie viele Leute den Hinweis "offensichtlich, da es keine Garantie gibt, dass die Threads in der Reihenfolge funktionieren, in der Sie sie starten" ignorieren und denken, dass sie es richtig verstanden haben.
Joa Ebert

Obwohl es wahr ist, dass "es keine Garantie gibt, dass die Threads in der Reihenfolge funktionieren, in der Sie sie starten", tun sie dies in der Praxis fast immer für ein so triviales Programm. Um diese Verwirrung zu vermeiden, füge ich dem Problem hinzu, dass jeder Thread 1) eine (kleine) zufällige Anzahl von Millisekunden warten muss 2) sein Zeichen ausgeben 3) auf eine andere zufällige (möglicherweise lange) Zeit warten muss Der Code funktioniert, indem er nur ein paar Mal ausgeführt wird. Und die join () -Lösungen würden viel schlechter abschneiden. Ohne zufälliges Warten könnte man durch einen erfolgreichen Lauf irregeführt werden, um zu glauben, dass sein Programm korrekt ist.
Silviot

Antworten:


10

APL (Dyalog) ( 44 43 39 34)

{⍞←⍺⊣⎕DL⍵}&⌿2 11⍴'Hello World',⍳11

Erläuterung:

  • 2 11⍴'Hello World',⍳11 Erstellt eine Matrix: (H, 1), (e, 2), ...
  • &⌿ bedeutet: für jede Spalte der Matrix machen Sie auf einem separaten Thread:
  • In einem Thread ist jetzt der Charakter und ist jetzt die Zeit
  • ⎕DL⊃⍵wartet Sekunden.
  • Dann ⍞←⍺gibt das Zeichen.

11
Weißt du was? Ich werde dein Wort dafür nehmen ... :)
Bolster

OK, das ist die kürzeste. Herzliche Glückwünsche!
Tharwen

19

C, 61 62 Zeichen

i;main(){write(1,"Hello World\n"+i++,1);i>13||fork()||main();}

Die Funktionen der pthread-Bibliothek haben alle laaaaange Namen, daher habe ich stattdessen für jedes Zeichen einen eigenen Prozess gestartet. fork()ist so viel kürzer.

Es musste write()statt verwendet werden, putchar()da die stdio-Pufferfunktionen nicht threadsicher sind.

Bearbeitet : Bis zu 62 Zeichen sichern. In meinem Eifer ließ das Abfallen auf 61 Zeichen auch die Fadensicherheit fallen.


Es sollte möglich sein, die Schreibanweisung write(1,"Hello World\n",!!++i)für 2 Bytes auf zu ändern . Ansonsten gute Lösung.
Primo

Sie sollten das versuchen und sehen, was es produziert.
Brotkasten

Mein Fehler, meinte ich!!++i
Primo

Das scheint das zu sein, was Sie das erste Mal geschrieben haben. Ich verstehe also nicht, welchen Fehler Sie korrigieren wollten. Und ich war nicht scherzhaft: Ich meinte ehrlich, Sie sollten es selbst versuchen und sehen, was passiert. Wenn Sie den Zusatz entfernen, gibt jeder Thread nur das erste Zeichen der Zeichenfolge aus.
Brotkasten

Ich hatte es ursprünglich geschrieben !!i++, es aber einige Sekunden später bearbeitet, da mir klar wurde, dass es 0bei der ersten Iteration ausgewertet werden würde . Ich nahm an, dass Sie die unbearbeitete Version gesehen hatten. Ich kann Ihren Code nicht testen, da er nur einmal das erste Zeichen ausgibt . Es gibt jedoch viele Alternativen. i++<13, using !!i, or evenwrite(1,"Hello World\n",i++>13||fork()||main())
primo

9

Ruby, 46 Zeichen

"Hello World".chars{|c|Thread.new{$><<c}.join}

Es wird synchronisiert, da das Programm auf das Ende des Threads wartet, bevor der nächste Thread gestartet und mit dem nächsten Zeichen fortgefahren wird.


7

Pythonect (35 Zeichen)

http://www.pythonect.org

"Hello World"|list|_.split()->print

Dies ist die bisher kürzeste. Da ich keine Ahnung habe, was es tatsächlich tut, gehe ich davon aus, dass es korrekt ist, und akzeptiere es in ein oder zwei Tagen, wenn sich niemand dagegen ausspricht oder etwas Kürzeres veröffentlicht.
Tharwen

1
Schauen Sie sich nur die Beispiele an. Sollte die print-Anweisung oder die list-Anweisung nicht mit eckigen Klammern [] versehen sein?
Dalin Seivewright

1
Hallo, ich leite Itziks (Schöpfer von Pythonect) Antwort weiter: '->' und '|' sind beide Pythonect-Operatoren. Der Pipe-Operator übergibt ein Element an ein Element, während der andere Operator alle Elemente gleichzeitig übergibt. Was das obige Programm macht, ist, es braucht "Hello World" -String, verwandelt es in eine Liste, teilt die Liste in Zeichen und sendet jedes Zeichen zum Drucken. Es ist möglich, das Programm noch weiter zu optimieren: iter ("Hello World") | Mit print wird die Zeichenfolge "Hello World" durchlaufen und jedes Zeichen zum Drucken gesendet (synchronisiert / blockiert). Viele Grüße, Itzik Kotler | ikotler.org
Leon Fedotov

wie wird das threading hier gemacht ???
Rohit

1
Wie @LeonFedotov erwähnt und aus der Pythonect-Quelle (verfügbar bei Pythonect ) nach dem Parsen für jede Iteration und den Operator '->' erfolgt das Threading folgendermaßen: thread = threading.Thread (target = __ run, args = ([( operator, item)] + expression [1:], copy.copy (globals_), copy.copy (locals_), return_value_queue, nicht iterate_literal_arrays)) thread.start ()
Jonathan Rom

6

Python ( 101 93 98)

Dies ist Peter Taylors Lösung. Das Drucken des N-ten Zeichens wird um N Sekunden verzögert. Zeige Kommentare.

import sys.threading as t
for x in range(11):t.Timer(x,sys.stdout.write,"Hello World"[x]).start()

Dies ist das Original:

import sys,threading as t
for x in "Hello World":t.Thread(None,sys.stdout.write,x,x).start()

Es hat funktioniert, weil das Drucken eines einzelnen Zeichens weniger Zeit in Anspruch nimmt als das Initialisieren eines neuen Threads in Python. Daher wurde der N-te Thread beendet, bevor der N + 1-te Thread erstellt wurde. Offensichtlich verstößt es gegen die Regeln, sich darauf zu verlassen.


Sie können 3 Zeichen sparen, indem Sie in ändern import sys,threading, import sys,threading as tund Sie können 2 weitere Zeichen sparen, indem Sie die Argumente als Positionsargumente anstelle von Schlüsselwortargumenten an Thread übergeben.
Joel Cornett

2
Wo ist der Code, der sich mit Thread-Sicherheit befasst? Sie feuern Threads nur in der Hoffnung ab, dass sie in derselben Reihenfolge ablaufen, in der Sie sie starten. Dies wird nicht immer der Fall sein und ist in der Tat der "schwierige Teil" dieses Problems. Anstatt Ihre Programmgröße zu optimieren, sollten Sie das Problem überdenken: Sie haben es gar nicht erst verstanden. Einen Beweis dafür, dass dieser Code nicht funktioniert, finden Sie unter gist.github.com/2761278 .
Silviot

Schnelle Lösung. Verwenden Sie threading.Timeranstelle von threading.Thread. Geben Sie xals Schlafparameter ein.
Joel Cornett

1
Joels Vorschlag kann um 4 to verbessert werdenfor x in range(11):t.Timer(x,sys.stdout.write,"Hello World"[x]).start()
Peter Taylor

1
@silviot: Ich habe die Tatsache ausgenutzt, dass das Erstellen eines Threads das Instanziieren eines Objekts umfasst und daher auf den von mir getesteten Systemen ein bis zwei Drittel einer Millisekunde dauert. Die Zeichenausgabe hat diesen Overhead nicht, was nur ein Zehntel dieser Zeit in Anspruch nimmt. Daher wird es "immer" funktionieren, solange Sie nichts überschreiben. Stdout ist gepuffert, damit es auch nie Probleme gibt.
Marinus

4

C # 73

"hello world".ToList().ForEach(c=>Task.Run(()=>Console.Write(c)).Wait());

Ich bin mir nicht sicher, ob dies die Anforderung erfüllt, dass jeder Buchstabe über einen eigenen Thread gedruckt wird, da die tpl Threads möglicherweise wiederverwenden kann.
Statichippo

Theoretisch haben Sie recht, aber auf meinem PC gibt ThreadPool.GetMaxThreads oder ThreadPool.GetAvailableThreads einen Wert um 1000 für E / A- und Arbeitsthreads zurück.
JJoos

4

APL (Dyalog Unicode) , 28 Byte SBCS

Volles Programm. Druckt nach stderr. Inspiriert von Marinus 'Lösung .

'Hello World'{⍞←⍺⊣⎕DL⍵}&¨⍳11

Probieren Sie es online!

⍳11 erste 11 ganze Zahlen

'Hello World'{}&¨ Erzeugen Sie für jede Ganzzahl als rechtes Argument ( ) die folgende Funktion mit dem entsprechenden Zeichen als linkes Argument ( ):

⎕DL⍵d e l ay richtige Argument Sekunden

⍺⊣ Verwerfen Sie diese (die effektive Verzögerung) zugunsten des Zeichens mit dem linken Argument

⍞← drucke das auf stdout ohne abschließenden Zeilenumbruch


wie wäre es ⍞∘←&¨'dlroW olleH'? - Ich weiß nicht, ob es theoretisch garantiert ist, aber es scheint, dass es sie immer in der richtigen Reihenfolge
druckt

@ngn Da es keine Garantie dafür gibt, dass die Threads in der Reihenfolge ausgeführt werden, in der Sie sie starten, müssen Sie Ihren Programm-Thread sicher machen, um sicherzustellen, dass die Ausgabe in der richtigen Reihenfolge gedruckt wird.
Adám

Das ist die Einschränkung, die ich ansprechen wollte, es könnte garantiert sein. Ich habe dies 100 Mal ausgeführt und es sieht so aus, als würde der Thread-Scheduler die Threads immer in umgekehrter Reihenfolge abholen. Oder zumindest ist das der Fall, wenn es ≤11 Aufgaben gibt. AFAIK ⍞∘←ist nicht unterbrechbar (oder? Vielleicht können Sie einen C-Entwickler fragen?). Dyalog Arbeitsgeräte grüne Fäden - 1 real Gewinde vorgibt viele zu sein, so dass , wenn ein (grün) Threadwechsel nicht stattfinden kann, ist die Reihenfolge ist vorhersagbar.
ngn

3

Java (160 Zeichen)

class A{static int i;public static void main(String...a){new Thread(){public void run(){System.out.print("Hello World".charAt(i++));if(i<11)main();}}.start();}}

Ja, ich weiß, das ist die falsche Sprache für Code-Golf. Ich mache es zum Spaß.


class A{public static void main(String[]args){new B(0).start();}}class B extends Thread{int i;B(int j){i=j;}public void run(){System.out.print("Hello World".charAt(i));if(i<10)new B(i+1).start();}}-197 Zeichen
Prince John Wesley

@Prince Yep, danke für die Korrektur!
Malcolm

class A extends Thread{static int i;public static void main(String[]args){System.out.print("Hello World".charAt(i++));if(i<11)new A().start();}public void run(){main(null);}}- 174 Zeichen
Wouter Coekaerts

@Wouter Sehr gut! Ich habe es total vermisst.
Malcolm

1
@ Malcolm, @ bkil, @ Wouter: class A{static int i;public static void main(String...a){new Thread(){public void run(){System.out.print("Hello World".charAt(i++));if(i<11)main();}}.start();}}- 160 Zeichen
Prince John Wesley

2

Bash (64)

:(){ [ "$1" ]&&(echo -n "${1:0:1}"&: "${1:1}")};: "Hello World"

@ Marinus Golf Doen von 3 Zeichen::()([ "$1" ]&&(printf "${1:0:1}"&: "${1:1}"));: Hello\ World
Digital Trauma

@fossilet Funktioniert für mich sowohl unter Linux als auch unter OSX, verschiedene Bash-Versionen. Die letzten ein oder zwei Zeichen werden manchmal nach der Shell-Eingabeaufforderung gedruckt.
Digital Trauma

2

Haskell ( 120 118)

import Control.Concurrent
t=threadDelay.(*3^8)
main=(\(x,y)->forkIO$t x>>putChar y)`mapM_`zip[0..]"Hello World">>t 99

Ich bin mir nicht ganz sicher, ob ich mit 9999 multiplizieren soll - ich habe einen 2-GHz-Xeon, auf dem es auch dann funktioniert, wenn Sie es nicht tun. überhaupt nichts tun.)


Speichern Sie 2 Zeichen, indem Sie (*5^6)anstelle von (*9999)und ohne Anführungszeichen für verwenden mapM_.
Mechanische Schnecke

@Mechanicalsnail Wenn Sie die Backticks entfernen, benötigen Sie ein zusätzliches Paar Klammern, andernfalls wird analysiert, (((mapM_ (\(x,y) ... )) zip) [0..]) ...was Sie nicht möchten.
Marinus

Was 999, könnte es auf 0 wegen Beschränkungen des Betriebssystems abgeschnitten zu werden , aber ich könnte falsch sein. Welches Betriebssystem verwenden Sie?
Joey Adams

2

Scala ( 81 79 Zeichen)

def?(l:String){if(l.size!=0)new Thread{print(l(0));?(l.tail)}}
?("Hello World")

Ein bisschen kürzer: def? (L: String) {if (l.size> 0) neuer Thread {print (l (0));? (L.tail)}}
Joa Ebert

@JoaEbert: ja, nett
Prince John Wesley


1

D (135 Zeichen)

import std.concurrency,std.stdio;
void main(){
    p("Hello World");
}
void p(string h){
    write(h[0]);
    if(h.length>1)spawn(&p,h[1..$]);
}

Ich starte den nächsten Thread erst, wenn ich das aktuelle Zeichen bereits gedruckt habe

bearbeite +2 Zeichen für eine bessere Kontrolle


Ich bekomme einen core.exception.RangeError@test.d(6): Range violationFehler.
Fish Monitor

@fossilet Ich habe es behoben
Ratschenfreak

1

Scala 74

"Hello World".zipWithIndex.par.map(x=>{Thread.sleep(x._2*99);print(x._1)})
  • zipWithIndex erzeugt ((H, 0), (e, 1), (l, 2) ...).
  • par macht es zu einer parallelen Sammlung.

Tests:

(1 to 10).foreach {_ => "Hello World".zipWithIndex.par.map(x=>{Thread.sleep(x._2*99);print(x._1)});println()}
Hello World
Hello World
Hello World
...
Hello World

scala> "Hello World".zipWithIndex.par.foreach(x=>{Thread.sleep(x._2*99);print(x._1)}) Hel lWrolod- Ich habe das
Prince John Wesley

Auch println(Thread.currentThread.getName)zeigt , dass die Fäden sind nicht eindeutig.
Prince John Wesley

@PrinceJohnWesley: Ich denke, Sie brauchen einen Kern pro Buchstabe, damit par die Arbeit auf alle Kerne verteilt.
Benutzer unbekannt

Ich hab es geschafft. also ein kern pro buchstabe + hohe auflösung der systemuhr erforderlich.
Prince John Wesley

Verwenden Sie mapanstelle von foreach. Sie können 4 Zeichen speichern.
Prince John Wesley

1

Javascript (72)

(function f(){console.log("Hello world"[i++]);i<11&&setTimeout(f)})(i=0)

1

scala (45)

Thread # Join-basierte Lösung

"Hello World"map{x=>new Thread{print(x)}join}

oder

for(x<-"Hello World")new Thread{print(x)}join

1

Dies ist mein F # -Versuch. Mein erstes ernstes F # Programm. Bitte sei nett.

let myprint c = async {
        printfn "%c"c
}
"Hello World"|>Seq.map myprint|>Async.Parallel|>Async.RunSynchronously|>ignore

0

Gehen

package main
import"fmt"
func main(){o:=make(chan int)
for _,c:=range"Hello World"{go func(c rune){fmt.Printf("%c",c)
o<-0}(c)}
for i:=0;i<11;i++{<-o}}

Sie müssen die Zeichen nicht zählen - wir erledigen das für Sie!
Benutzer unbekannt

0

Erlang (90)

-module(h).
r()->r("Hello World").
r([])->'';r([H|T])->spawn(h,r,[T]),io:format("~c",[H]).

Kompilieren erlc +export_all h.erl



0

Python: zu viele Zeichen, aber es funktioniert.

# Ok. First we patch Threading.start to test wether our solution actually works

import threading
import random, time
original_run = threading.Thread.run


def myRun(self):
    tosleep = random.randint(0,200)/1000.0
    time.sleep(tosleep)
    original_run(self)

threading.Thread.run = myRun

# And now the real code:
import time, sys, threading
string_to_write = "Hello World\n"
current_char_index = 0 # This integer represents the index of the next char to be written
# It will act as a semaphore: threads will wait until it reaches
# the index of the single char that particular thread is due to output

class Writer(threading.Thread):
    def __init__(self, char_to_write, index_to_write):
        self.char_to_write, self.index_to_write = char_to_write, index_to_write
        super(Writer, self).__init__()
    def run(self):
        ch = globals()['current_char_index']
        while not self.index_to_write == ch:
            time.sleep(0.005)
        sys.stdout.write(self.char_to_write)
        # This will be atomic because no other thread will touch it while it has "our" index
        globals()['current_char_index'] += 1

for i, char in enumerate(string_to_write):
    Writer(char, i).start()

Ich verstehe nicht, was der Zweck der Randomisierung der Schlafzeit ist.
Joel Cornett

@Joel, um sicherzustellen, dass die Threads, wenn sie funktionieren, nicht zufällig in der Reihenfolge ausgeführt werden, in der sie ausgelöst wurden.
Silviot


0

Ziel-C (183 Zeichen)

-(void) thread {[self performSelectorInBackground:@selector(printC) withObject:nil];}
-(void) printC {char *c = "Hello World"; for(int i = 0; c[i] != '\0'; i++) {printf("%c", c[i]);}}

0

Haskell 99 Zeichen

import Control.Concurrent
f[]=return()
f(x:xs)=(putChar x)>>(forkIO$f xs)>>f[]
main=f"Hello World"

Wie es funktioniert, beginnt jeder Thread am nächsten, nachdem er seinen Charakter angezeigt hat, so dass tatsächlich nützliche Dinge nicht außerhalb der Reihenfolge passieren können.


0

Bash , 43 Bytes

xargs -n1 printf<<<'H e l l o \  W o r l d'

Probieren Sie es online!

xargsGibt printffür jedes Zeichen einen eigenen Prozess aus (und wartet, bis es beendet wird).

Bash , 45 Bytes, keine externen Dienstprogramme

eval \(printf\ {H,e,l,l,o,\\\ ,W,o,r,l,d}\)\;

Probieren Sie es online!

Erweitert bis (printf H); (printf e); (printf l); (printf l); (printf o); (printf \ ); (printf W); (printf o); (printf r); (printf l); (printf d);vor der Auswertung. Die Klammern machen Bash Fork zu einer Unterschale für jeden Buchstaben (und warten, bis er beendet ist), aber diesmal printfist der Bash eingebaut.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.