/
description.ru.yml
130 lines (103 loc) · 5.89 KB
/
description.ru.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
---
name: Почтовый ящик процессов
theory: |
Как было упомянуто в прошлом занятии, процессы общаются между собой с помощью *сообщений*. Сообщения всегда отправляются *асинхронно*, без подтверждения доставки сообщения процессу. Чтобы получить результат обработки сообщения другим процессом, используется блокировка в ожидании ответного сообщения от другого процесса, но это уже тонкости реализации синхронного вызова. По сути способ синхронизации результата это низкоуровневые детали, с которыми сталкиваться скорее всего не придется. Для отправки сообщений используется функция `send`. Каждый процесс имеет собственный *почтовый ящик*, который он читает с помощью функции `receive`. Рассмотрим на примере:
```elixir
send(self(), {:hello, "world"})
# => {:hello, "world"}
receive do
{:hello, msg} -> msg
{:world, _} -> "unmatched expression"
end
# => "world"
```
Блок кода `receive` при получении сообщения, проходится по всему почтовому ящику в поиске совпадений по шаблону сообщений.
Отправка сообщений через `send` является неблокирующей операцией, то есть сообщения процессу отправляются *асинхронно*. Так как весь Elixir код выполняется в каком-то процессе, то процесс может отправлять сообщения самому себе.
Почтовые ящики у процессов могут переполняться, поэтому важно указывать базовый паттерн, в который попадут все остальные сообщения из ящика. Блок `receive` закончит выполнение при первом совпадении паттерна, в ином случае процесс заблокируется в ожидании подходящего сообщения, либо, по истечению указанного таймаута:
```elixir
receive do
{:hello, msg} -> msg
after
1_000 -> "message await stopped after 1 second"
end
# => "message await stopped after 1 second"
```
Теперь создадим отдельный процесс, который отправит сообщение в процесс родитель:
```elixir
parent = self()
# => #PID<0.105.0>
spawn(fn -> send(parent, {:hello, self()}) end)
# => #PID<0.6946.12>
receive do
{:hello, pid} -> "Got hello from #{inspect(pid)}"
end
# => "Got hello from #PID<0.6946.12>"
```
Так как `receive` завершается по таймауту или успешной обработке сообщения, то как сделать обработку процессом сообщений постоянной? Рекурсивный вызов `receive`, конечно же!
```elixir
defmodule Processor do
def process() do
receive do
{:hello, msg} ->
IO.puts(msg)
process()
{:world, msg} ->
IO.puts(String.upcase(msg))
process()
{:exit, _} -> IO.puts("Shutdown processor...")
end
end
end
processor = spawn(fn -> Processor.process() end)
# => #PID<0.6952.12>
Process.alive?(processor)
# => true
send(processor, {:hello, "code basic"})
# для просмотра почтового ящика родительского процесса
# далее по коду будет подразумеваться, что эта строчка кода
# будет вызываться после каждой отправки сообщения другому процессу
Process.info(parent, :messages)
# => code basic
Process.alive?(processor)
# => true
send(processor, {:world, "code basic"})
# => CODE BASIC
Process.alive?(processor)
# => true
send(processor, {:exit, ""})
# => Shutdown processor...
Process.alive?(processor)
# => false
```
instructions: |
Создайте функцию `calculate` которая принимает процесс-получатель и поддерживает операции `:+`, `:-`, `:*` в виде сообщений от процесса и при обработке возвращает сообщение процессу отправителю с результатом, функция должна поддерживать постоянную обработку сообщений. При передаче сообщения `:exit`, функция перестает обрабатывать входящие сообщения:
```elixir
parent = self()
calculator = spawn(fn -> Solution.calculate(parent) end)
send(calculator, {:+, [2, 5]})
receive do
{:ok, result} -> result
end
# => 7
Process.alive?(calculator)
# => true
send(calculator, {:*, [2, 5]})
receive do
{:ok, result} -> result
end
# => 10
Process.alive?(calculator)
# => true
send(calculator, {:exit})
receive do
{:ok, result} -> result
end
# => :exited
Process.alive?(calculator)
# => false
```
tips:
- |
[Конкурентность на примере процессов Elixir](https://www.youtube.com/watch?v=A4x6IfceJCM)
- |
[Официальная документация](https://hexdocs.pm/elixir/Process.html)