概述 之前學習的 Agent,GenSever以及GenEvent,都是用來管理狀態或者處理消息的。 但是在很多時候,我們需要的是執行某個任務,這時如果使用 GenSever 或者 GenEvent,就會顯得比較笨重。 這時,我們就可以使用 Task 模塊,使用 Task 模塊時註意以下幾點: 1. ...
概述
之前學習的 Agent,GenSever以及GenEvent,都是用來管理狀態或者處理消息的。
但是在很多時候,我們需要的是執行某個任務,這時如果使用 GenSever 或者 GenEvent,就會顯得比較笨重。
這時,我們就可以使用 Task 模塊,使用 Task 模塊時註意以下幾點:
- 每個 task 只執行一個特定的功能,要讓 task 處理的業務儘量簡單(如果業務複雜的話,考慮使用 GenSever 或者 GenEvent)
- task 之間儘量不要交互,也儘量不要和其他 process交互 ,保持 task 的獨立性
task 最重要的特性是能夠方便的將順序執行的代碼轉變為併發執行的代碼。
task 示例
示例一: 將順序代碼轉為併發代碼(類似多線程)
defmodule TaskTest do
def test_sync() do
for n <- [1,2,3,4] do
:timer.sleep(1000)
IO.puts("it's #{n} #{TimeUtil.now}")
end
end
def test_async() do
for n <- [1,2,3,4] do
Task.start_link(fn ->
:timer.sleep(1000)
IO.puts("it's #{n} #{TimeUtil.now}")
end)
end
end
defmodule TimeUtil do
def now() do
{{y,m,d}, {h,mm,s}} = :calendar.local_time
"#{y}/#{m}/#{d} #{h}:#{mm}:#{s}"
end
end
IO.puts "execute sync"
TaskTest.test_sync
IO.puts "==============================="
IO.puts "execute async"
TaskTest.test_async
執行結果如下:
iex(1)> r(TaskTest)
execute sync
it's 1 2016/5/31 13:53:2
it's 2 2016/5/31 13:53:3
it's 3 2016/5/31 13:53:4
it's 4 2016/5/31 13:53:5
===============================
execute async
it's 1 2016/5/31 13:53:6
it's 2 2016/5/31 13:53:6
it's 3 2016/5/31 13:53:6
it's 4 2016/5/31 13:53:6
示例二:非同步執行,並獲取執行結果
defmodule TaskTest do
def exec_task() do
t = Task.async(fn ->
IO.puts("start to do something at #{TimeUtil.now}")
:timer.sleep(3000)
end)
t
end
def get_task_result(t) do
Task.await(t, 5000)
IO.puts("get something result at #{TimeUtil.now}")
end
end
defmodule TimeUtil do
def now() do
{{y,m,d}, {h,mm,s}} = :calendar.local_time
"#{y}/#{m}/#{d} #{h}:#{mm}:#{s}"
end
end
t = TaskTest.exec_task # 非同步執行
IO.inspect(TaskTest.get_task_result(t)) # 同步獲取執行結果
運行結果如下:
iex(1)> r(TaskTest)
start to do something at 2016/5/31 14:24:48
get something result at 2016/5/31 14:24:51
:ok
註意 如果 get_task_result 中 Task.await 的超時時間設置的小於task的執行時間的話(比如await的時間由 5000 -> 2000),
那麼,會導致 get_task_result timeout的錯誤。
總結
從上面的例子可以看出,利用 Task 模塊,可以很方便的實現併發和非同步操作。
但是,在用 task 執行任務的時候,我們發現,在併發和非同步的環境中,如果某個 task 執行失敗的話,甚至會導致主進程也失敗。
下一節的監督者機制,將介紹 elixir 如何利用 OTP平臺 完美解決上述併發和非同步中的問題。