zulkris
Why sleep (Process.sleep or :timer.sleep) freed process messages?
Have code:
single_api_call = fn ->
sleep_time = Enum.random(554..2944)
Process.sleep(sleep_time) # this line affects results
sleep_time
end
defmodule Worker do
def start(fun) do
pid = self()
send(pid, {self(), fun.()})
end
end
Enum.to_list(1..10)
|> Enum.map(
fn _num ->
spawn Worker, :start, [single_api_call]
end)
Process.sleep(3000)
Process.info(self(), :messages) |> IO.inspect()
please see at line with Process.sleep.
Now code works this way:
[#PID<0.103.0>, #PID<0.104.0>, #PID<0.105.0>, #PID<0.106.0>, #PID<0.107.0>,
#PID<0.108.0>, #PID<0.109.0>, #PID<0.110.0>, #PID<0.111.0>, #PID<0.112.0>]
{:messages, []}
messages is empty! why?
after deleting line with Process.sleep() it works fine:
[#PID<0.103.0>, #PID<0.104.0>, #PID<0.105.0>, #PID<0.106.0>, #PID<0.107.0>,
#PID<0.108.0>, #PID<0.109.0>, #PID<0.110.0>, #PID<0.111.0>, #PID<0.112.0>]
{:messages,
[
{#PID<0.103.0>, 2049},
{#PID<0.104.0>, 619},
{#PID<0.105.0>, 2854},
{#PID<0.106.0>, 2584},
{#PID<0.107.0>, 706},
{#PID<0.109.0>, 2700},
{#PID<0.110.0>, 2397},
{#PID<0.111.0>, 2303},
{#PID<0.112.0>, 1863}
]}
Marked As Solved
joey_the_snake
Actually my bad. There is a bug in your code. The Worker is sending the message to itself instead of the main process. You have to do this
defmodule Worker do
def start(pid, fun) do
send(pid, {self(), fun.()})
end
end
Enum.to_list(1..10)
|> Enum.map(
fn _num ->
spawn Worker, :start, [self(), single_api_call]
end)
Also Liked
rvirding
Yes, back in the old days before multicore when there was only one scheduler we had very strict requirements that the system should NEVER block. So it is as @ChrisYammine says the scheduler forces processes to yield control, either after a fixed number of reductions (function calls), waiting for a message or timeout and other things. This is why you have never had to think about making sure a process doesn’t block the system. It can’t.
This of course just works in the same way with many schedulers.
There is a lot, A LOT, of effort put into the BEAM implementation to make this work efficiently and painlessly.
joey_the_snake
pure guessing, but maybe the number of cpus assigned to the docker container is too small and so the spawned tasks don’t get scheduled until after the main process is finished
rvirding
The reason is that in Worker.start/ the function defined in single_api_call is called before a message is sent. This is because that function is called when creating the message to be sent {self(), fun.()} and the send requires all ita arguments to be evaluated which means it waits until the function returns which is after it has slept.
dimitarvp
I’ll be the guy to ask:
What is it that you’re truly aiming at?
There are good job queue / worker pool libraries, we can point you at some of them if you don’t want to roll your own.
bdarla
As it is also recommended in the hex docs, using sleep shall be avoided.
If you want to use the first sleep to “simulate” something, leave it there for your testing.
However, the sleep of the parent process can be relaxed with a receive as suggested in the aforementioned Hexdocs, e.g. with a receive:
parent = self()
Task.start_link(fn ->
do_something()
send(parent, :work_is_done)
...
end)
receive do
:work_is_done -> :ok
after
# Optional timeout
30_000 -> :timeout
end
In this example from Hexdocs, replace do_something with single_api_call and the Task with your Process.







