it-swarm.com.de

Frage zum Multithreading-Synchronisationsinterview: Finde n Wörter mit m Threads

Gibt es eine Möglichkeit, wie dieses Problem von einer Lösung mit mehreren Threads anstelle eines einzelnen Threads profitieren kann?


In einem Interview wurde ich gebeten, ein Problem mit mehreren Threads zu lösen. Es scheint mir, dass die mehreren Threads keinen Nutzen bringen.

Hier ist das Problem:

Sie erhalten einen Absatz, der n Wörter enthält, Sie erhalten m Threads. Was Sie tun müssen, ist, dass jeder Thread ein Wort drucken und die Steuerung dem nächsten Thread geben sollte. Auf diese Weise druckt jeder Thread weiterhin ein Wort. Falls der letzte Thread kommt, sollte er den ersten Thread aufrufen. Der Druckvorgang wird wiederholt, bis alle Wörter im Absatz gedruckt sind. Schließlich sollten alle Threads ordnungsgemäß beendet werden. Welche Art der Synchronisation wird verwendet?

Ich bin der festen Überzeugung, dass wir hier keine Threads nutzen können, glaube aber, dass der Interviewer versucht, meine Synchronisationsfähigkeiten zu messen. Vermisse ich etwas in diesem Problem, das dazu führen würde, dass mehrere Threads einen Wert haben?

Kein Code erforderlich, nur ein paar Gedanken. Ich werde selbst implementieren.

23
rplusg

Es klingt für mich so, als würden sie Sie zu einer Semaphorlösung führen. Semaphoren werden verwendet, um einem anderen Thread zu signalisieren, dass sie an der Reihe sind. Sie werden viel seltener verwendet als Mutexe, weshalb sie meiner Meinung nach eine gute Interviewfrage sind. Es ist auch der Grund, warum das Beispiel erfunden zu sein scheint.

Grundsätzlich würden Sie m Semaphore erstellen. Jeder Thread x wartet auf das Semaphor x und postet dann auf dem Semaphor x+1 nach seiner Sache. Im Pseudocode:

loop:
    wait(semaphore[x])
    if no more words:
        post(semaphore[(x+1) % m])
        exit
    print Word
    increment current Word pointer
    post(semaphore[(x+1) % m])
21
Karl Bielefeldt

Meiner Meinung nach ist dies eine fabelhafte Interviewfrage - zumindest unter der Annahme, dass (1) vom Kandidaten ein tiefes Wissen über das Threading erwartet wird und (2) der Interviewer auch über tiefes Wissen verfügt und die Frage verwendet, um den Kandidaten zu untersuchen. Es ist immer möglich, dass der Interviewer nach einer bestimmten, engen Antwort suchte, aber ein kompetenter Interviewer sollte nach Folgendem suchen:

  • Fähigkeit, abstrakte Konzepte von konkreter Umsetzung zu unterscheiden. Ich werfe diesen hauptsächlich als Metakommentar zu einigen Kommentaren ein. Nein, es ist nicht sinnvoll, eine einzelne Liste von Wörtern auf diese Weise zu verarbeiten. Das abstrakte Konzept einer Pipeline von Operationen, die mehrere Maschinen mit unterschiedlichen Fähigkeiten umfassen kann, ist jedoch wichtig.
  • Nach meiner Erfahrung (fast 30 Jahre verteilte Anwendungen, Anwendungen mit mehreren Prozessen und Anwendungen mit mehreren Threads) ist das Verteilen der Arbeit nicht der schwierige Teil. Beim Sammeln der Ergebnisse und Koordinieren unabhängiger Prozesse treten die meisten Threading-Fehler auf (meiner Erfahrung nach auch hier). Indem der Interviewer das Problem auf eine einfache Kette reduziert, kann er sehen, wie gut der Kandidat über Koordination denkt. Außerdem hat der Interviewer die Möglichkeit, alle möglichen Anschlussfragen zu stellen, z. B. "OK, was ist, wenn jeder Thread sein Wort zur Rekonstruktion an einen anderen Thread senden muss?".
  • Denkt der Kandidat darüber nach, wie sich das Speichermodell des Prozessors auf die Implementierung auswirken könnte? Wenn die Ergebnisse einer Operation niemals aus dem L1-Cache gelöscht werden, ist dies ein Fehler, auch wenn keine offensichtliche Parallelität vorliegt.
  • Trennt der Kandidat das Threading von der Anwendungslogik?

Dieser letzte Punkt ist meiner Meinung nach der wichtigste. Aufgrund meiner Erfahrung wird es wieder exponentiell schwieriger, Thread-Code zu debuggen, wenn das Threading mit der Anwendungslogik gemischt wird (sehen Sie sich einfach alle Swing-Fragen unter SO für Beispiele) an. I. glauben, dass der beste Multithread-Code als in sich geschlossener Single-Thread-Code mit klar definierten Übergaben geschrieben ist.

In diesem Sinne würde mein Ansatz darin bestehen, jedem Thread zwei Warteschlangen zuzuweisen: eine für die Eingabe und eine für die Ausgabe. Der Thread blockiert beim Lesen der Eingabewarteschlange, nimmt das erste Wort aus der Zeichenfolge und übergibt den Rest der Zeichenfolge an die Ausgabewarteschlange. Einige der Merkmale dieses Ansatzes:

  • Der Anwendungscode ist dafür verantwortlich, eine Warteschlange zu lesen, etwas mit den Daten zu tun und die Warteschlange zu schreiben. Es ist egal, ob es sich um Multithreading handelt oder nicht oder ob es sich bei der Warteschlange um eine speicherinterne Warteschlange auf einem Computer oder um eine TCP-basierte Warteschlange zwischen Computern handelt, die sich auf gegenüberliegenden Seiten der Welt befinden.
  • Da der Anwendungscode wie Single-Threaded geschrieben ist, kann er deterministisch getestet werden, ohne dass viel Gerüst erforderlich ist.
  • Während der Ausführungsphase besitzt der Anwendungscode die zu verarbeitende Zeichenfolge. Die Synchronisation mit gleichzeitig ausgeführten Threads muss nicht berücksichtigt werden.

Trotzdem gibt es immer noch viele Grauzonen, die ein kompetenter Interviewer untersuchen kann:

  • "OK, aber wir möchten Ihr Wissen über Parallelitätsprimitive sehen. Können Sie eine Blockierungswarteschlange implementieren?" Ihre erste Antwort sollte natürlich sein, dass Sie eine vorgefertigte Blockierungswarteschlange von der Plattform Ihrer Wahl verwenden. Wenn Sie jedoch Threads verstehen, können Sie eine Warteschlangenimplementierung in weniger als einem Dutzend Codezeilen erstellen, wobei Sie die von Ihrer Plattform unterstützten Synchronisationsprimitive verwenden.
  • "Was ist, wenn ein Schritt im Prozess sehr lange dauert?" Sie sollten sich überlegen, ob Sie eine begrenzte oder unbegrenzte Ausgabewarteschlange wünschen, wie Sie mit Fehlern umgehen können und welche Auswirkungen dies auf den Gesamtdurchsatz hat, wenn Sie eine Verzögerung haben.
  • So stellen Sie die Quellzeichenfolge effizient in die Warteschlange. Nicht unbedingt ein Problem, wenn Sie mit In-Memory-Warteschlangen arbeiten, kann aber ein Problem sein, wenn Sie zwischen Computern wechseln. Sie können auch schreibgeschützte Wrapper über einem zugrunde liegenden unveränderlichen Byte-Array untersuchen.

Wenn Sie Erfahrung in der gleichzeitigen Programmierung haben, können Sie über einige Frameworks (z. B. Akka für Java/Scala) sprechen, die diesem Modell bereits folgen.

22
kdgregory

Interviewfragen sind manchmal tatsächlich Trickfragen, die Sie zum Nachdenken über das Problem bringen sollen, das Sie lösen möchten. Fragen zu einer Frage zu stellen ist ein integraler Teil der Annäherung an jedes Problems. ob in der realen Welt oder in einem Interview. Im Internet gibt es eine Reihe von Videos, in denen erläutert wird, wie Fragen in technischen Interviews behandelt werden können (insbesondere bei Google und möglicherweise bei Microsoft).

"Versuch einfach zu antworten und verschwinde ..."

Wenn Sie sich Interviews mit diesem Gedankenmuster nähern, werden Sie jedes Interview für ein Unternehmen bombardieren, für das es sich zu arbeiten lohnt.

Wenn Sie nicht glauben, dass Sie viel gewinnen (wenn überhaupt etwas durch Threading), sagen Sie ihnen das. Sagen Sie ihnen , warum Sie glauben, dass es keinen Nutzen gibt. Besprechen Sie sich mit ihnen. Technische Interviews sollen eine offene Diskussionsplattform sein. Möglicherweise lernen Sie etwas darüber, wie nützlich sein kann. Machen Sie nicht einfach blindlings Fortschritte und versuchen Sie, etwas umzusetzen, was Ihnen Ihr Interviewer gesagt hat.

16
Demian Brecht

Wie Sie sagten, glaube ich nicht, dass dieses Szenario, wenn überhaupt, vom Threading stark profitiert. Es ist höchstwahrscheinlich langsamer als eine Implementierung mit einem Thread.

Meine Antwort wäre jedoch, dass jeder Thread in einer engen Schleife versucht, auf eine Sperre zuzugreifen, die den Zugriff auf den Word-Array-Index steuert. Jeder Thread greift nach der Sperre, ruft den Index ab, holt das entsprechende Word aus dem Array, druckt es aus, erhöht den Index und gibt die Sperre frei. Der Thread wird beendet, wenn sich der Index am Ende des Arrays befindet.

Etwas wie das:

while(true)
{
    lock(index)
    {
        if(index >= array.length())
          break;
        Console.WriteLine(array[index]);
        index++;
    }
}

Ich denke, dies sollte den einen Thread nach dem anderen erreichen, aber die Reihenfolge der Threads ist nicht garantiert. Ich bin gespannt auf andere Lösungen.

0
ConditionRacer
  • Markieren Sie zuerst den Absatz mit den entsprechenden Trennzeichen und fügen Sie die Wörter einer Warteschlange hinzu.

  • Erstellen Sie N Threads und bewahren Sie diese in einem Thread-Pool auf.

  • Iterieren Sie über den Thread-Pool und starten Sie den Thread und warten Sie auf die
    Thread zum Beitreten. Und starten Sie den nächsten Thread, sobald der erste Thread endet und so weiter.

  • Jeder Thread sollte nur die Warteschlange abfragen und drucken.

  • Sobald alle Threads im Thread-Pool verwendet wurden, beginnen Sie am Anfang des Pools.

0
java_mouse