2016-09-01 3 views
0

So begann ich mit Elixir zu spielen, und ich wollte zwei Module machen. Eine, die Sie auffordert, eine Nachricht einzugeben und dann die Nachricht an das andere Programm zu senden, das bestätigen würde, dass es die Nachricht erhalten hat, die das erste Programm veranlassen würde, die Nachricht auszudrucken.Drucken an Konsole von Elixir Process

Nun, ich habe Probleme.

Der Absender unten:

defmodule PracticeCaller do 
    def start do 
     spawn(&sloop/0) 
    end 

    def sloop do 
     pid = :erlang.spawn(&PracticeServer.start/0) 
     message = IO.gets "Enter a message:" 
     send(pid, {self, message}) 
     receive do 
      {_caller, reply} -> IO.puts "#{reply}" 
     end 
     sloop 
    end 
end 

und der Empfänger ist hier:

defmodule PracticeServer do 
    def start do 
     spawn(&loop/0) 
    end 

    defp loop do 
     receive do 
      {caller, "kill"} -> send(caller, {self, "Process Dead"}) 
       Process.exit(self, :kill) 
      {caller, _} -> send(caller, {self, "Thank you for your message!"})  
     end 

     loop  
    end 

end 

Mein erstes Problem ist, dass, wenn ich meine Absender Schleife starten, das Terminal fordert mich auf eine Nachricht eingeben, aber es druckt die empfangene Nachricht nicht.

Zweitens, wenn ich "kill" eingeben, friert das Terminal ein, weil ich nicht sicher bin, wie man eine: kill-Antwort behandelt.

Haben Sie Hilfe bei der Behebung dieser Probleme?

+0

Was soll der Client nach dem Serverausfall tun? – Dogbert

+0

Ich möchte, dass der Client auch getötet wird, aber ich bin mir nicht sicher, wie ich das machen soll. – Mascasc

Antwort

3

Das erste Problem ist:

pid = :erlang.spawn(&PracticeServer.start/0) 

und die Tatsache, dass PracticeServer.start/0 Anrufe spawn wieder. Dies bedeutet, dass sich pid hier nicht auf den Prozess bezieht, der PracticeServer.loop/0 ausführt, sondern einen Prozess, der PracticeServer.loop/0 hervorbrachte und sofort beendet wurde.

Wenn ich ändern:

pid = :erlang.spawn(&PracticeServer.start/0) 

nur:

pid = PracticeServer.start 

Einige Dinge anfangen zu arbeiten:

Enter a message:hello 
Thank you for your message! 
Enter a message:world 
Thank you for your message! 

Aber:

Enter a message:kill 
Thank you for your message! 

Dies liegt daran, IO.gets/1 enthält die abschließende Newline-Zeichen, was bedeutet, dass die {caller, "kill"} Muster in PracticeServer.loop/0 nie übereinstimmt.

Dies kann durch Änderung festgesetzt:

message = IO.gets "Enter a message:" 

zu

message = IO.gets("Enter a message:") |> String.trim_trailing 

Jetzt:

Enter a message:kill 
Process Dead 
Enter a message:kill 
Process Dead 

Ein weiteres Problem ist, dass Sie derzeit Prozesse undicht sind. Anstatt den erstellten Server wiederzuverwenden, erstellen Sie für jede Nachricht einen neuen Server. Die idiomatische Methode, eine rekursive receive zu behandeln, die "getötet" werden kann, besteht darin, den rekursiven Aufruf nicht auszuführen, wenn der Prozess beendet werden soll. Hier ist ein paar refaktorierter Code:

Beachten Sie, dass dies die VM nicht stoppt, auch nachdem Sie "kill" gesendet haben.Wenn dies nur ein Skript ist, können Sie :erlang.halt zu PracticeCaller.loop/1 hinzufügen, wenn die Nachricht "Process Dead" lautet, um die VM sofort anzuhalten.

+0

Oh wow, das ist super hilfreich. Vielen Dank. – Mascasc