2016-04-04 3 views
1

Я пытаюсь написать простую программу эликсира, которая может реагировать на ввод пользователя. Моя проблема в том, что чтение из stdio, похоже, не работает из Tasks. Если моя идея глупа, пожалуйста, покажите мне пример того, как это делается. Я ничего не могу найти в ИнтернетеПолучение пользовательского ввода в elixir Задача

я сломал мою проблему вплоть до простого примера:

t = Task.async((fn->IO.gets "what?" end))     
%Task{owner: #PID<0.65.0>, pid: #PID<0.80.0>, ref: #Reference<0.0.2.135>} 

Целевая запускается:

iex(4)> pid=Map.get(t, :pid) 
#PID<0.80.0> 
iex(5)> Process.alive? pid          
true 

и живым, но он не печатает к stdio и не читает. Это не выход из обычного или с исключением. Я тоже попробовал IO.read/2.

В моей программе задание запускается с Task.spawn_link/1, но проблема в том же .IO.gets/2 и код, следующий за функцией IO.gets/2, не выполняются.

Супервизор запуска задачи:

defmodule Prime do 
    use Application 

    def start(_type, _args) do 
     import Supervisor.Spec, warn: false 

children = [ 
    # Define workers and child supervisors to be supervised 
    worker(Task, [fn->Prime.IO.communicate(nil) end], restart: :transient), 
    supervisor(Prime.Test.Supervisor, []) 
] 

opts = [strategy: :one_for_one, name: Prime.Supervisor] 
Supervisor.start_link(children, opts) end end 

Функция задачи:

defmodule Prime.IO do 

@doc """ 
    handles communication with the user and user demanded Actions. 
""" 

def communicate(numTasks) do 
    case(numTasks) do 

    nil -> 
     {numTasks, _} =Integer.parse(IO.gets "This program searches for prime numbers per try and error.\nHow many concurrent Tasks?\n") 
     Prime.IO.communicate(numTasks) 

    x when is_number(x) -> 
     Prime.Test.Server.setTaskNumber(numTasks) 
     Prime.IO.communicate("waiting") 

    y when is_bitstring(y) -> 
     IO.puts(numTasks) 

    _ -> 
     Prime.IO.communicate(nil) 

    end 
end 
end 

ответ

0

Это минимальный код, который будет работать:

t = Task.async(fn -> IO.gets "What?" end) 
Task.await(t) 

В основном вам нужно await в возвращенная задача. Подробнее об async и ждут здесь: http://elixir-lang.org/docs/stable/elixir/Task.html#await/2

+0

спасибо за помощь ур и за исправление моей правдивости ужаса :) – Kasuyakema

5

В Elixir всегда есть один процесс обработки io. Если вы печатаете что-то в одном из процессов, он напрямую не записывает в stdout. Он отправляет сообщение процессу под названием «лидер группы». Даже если многие процессы пишут в то же время вы не можете увидеть сообщения вперемешку, как это:

This is This is message one 
message two 

Вы всегда получите чистый выход:

This is message one 
This is message two 

В случае ввода, может быть только один процесс чтения из stdin. Если вы используете iex session, это процесс оболочки. Если другие процессы хотят читать из stdin, они терпеливо ждут, пока iex не даст контроль над stdin.

Именно поэтому, если вы вызываете Task.await, функция волшебным образом работает. Это не потому, что ожидание начинает процесс. Await под капотом вызывает receive, который приостанавливает процесс, который вызвал его до появления сообщения. Shell отдает stdin, и теперь другие процессы могут его использовать, поэтому вы видите приглашение.

По умолчанию ожидание ждет 5 секунд для получения результатов, поэтому вам нужно быть быстрым.

Все эти проблемы - это просто потому, что вы используете интерактивную сессию. Если вы запускаете это нормально, все должно работать. Когда вы ждете для выполнения этой задачи, вы можете указать бесконечное время ожидания, как это:

result = Task.await(t, :infinity) 

Это не документированы в официальных Task документах, но почти все OTP таймаутов уважать соглашение с проходящим в бесконечность, как атом.

+0

Спасибо, что очень поморщился, помог вам указать мне в правильном направлении. Я думаю, что я не могу использовать Task.await, так как моя задача в программе порождена под деревом надзора, но мне удалось ее решить, приостановив мой первоначальный процесс с помощью: timer.sleep (: infinity) – Kasuyakema

Смежные вопросы