LoginSignup
2
1

More than 1 year has passed since last update.

Elixir Concurrent Programming(4) - Task and Agent

Last updated at Posted at 2018-09-28

■ ElixirのConcurrent Programmingについてまとめた過去記事
Elixir Concurrent Programming(1) - Spawn - Qiita
Elixir Concurrent Programming(2) - GenServer - Qiita
Elixir Concurrent Programming(3) - Supervisors - Qiita
Elixir Concurrent Programming(4) - Task and Agent -Qiita

 SpawnやGenServerなどより、簡単にバックグランド・プロセスをつくるための仕組みである、TaskとAgentについてみてみます。

1.Task

 GenServerはサーバとして働き、永続的にリクエストを受け付け、処理を行うというものでした。これに反してTaskはworkerプロセスとして起動され、リクエストを受け取ることもなく、与えられた仕事が終了したら停止しするものです。具体的には、Taskは関数をバックグラウンドのプロセスとして走らせます。Taskには2種類あって、resultを返すものと返さないものがあります。前者をawaited taskと呼びます。

 (補足)ここで述べるのは基本的な事柄ですので必要に応じて「Task - Hexdocs」を参照してください。

 awaited taskTask.asyncで起動し、Task.awaitで返答を受け取ります。

 以下に簡単な例を示します。特に次のことに注目します。Task.awaitの順番に関して、worker40とworker37を入れ替えます。当然、worker37の方が完了が早いはずですが、上から順番に実行されるので、worker40のawaitを待ってworker37のawaitが実行されます。

mytask.ex
defmodule Fib do
  def fib(0), do: 0
  def fib(1), do: 1
  def fib(n), do: Fib.fib(n-1) + Fib.fib(n-2)
end

### 関数Fib.fibに引数を与えて起動します
worker35 = Task.async(fn -> Fib.fib(35) end)
worker37 = Task.async(fn -> Fib.fib(37) end)
worker40 = Task.async(fn -> Fib.fib(40) end)

### Task.awaitはworkerの仕事の完了を待ちます。
IO.puts "worker35を待つ"
result35 = Task.await(worker35,50000) ### timeout=500000
IO.puts "worker35の結果 = #{result35}"

IO.puts "worker40を待つ"
result40 = Task.await(worker40,50000) ### timeout=500000
IO.puts "worker40の結果 = #{result40}"

IO.puts "worker37を待つ"
result37 = Task.await(worker37,50000) ### timeout=500000
IO.puts "worker37の結果 = #{result37}"

 以下が実行結果です。

# elixir mytask.ex
worker35を待つ ### ちょっと待って表示されます
worker35の結果 = 9227465
worker40を待つ ### 結構待ちます
worker40の結果 = 102334155
worker37を待つ ### 既に結果が出ているのですぐ表示されます
worker37の結果 = 24157817

 Non-awaited taskはTask.asyncでなく、Task.start_linkで呼び出されます。簡単に以下のような感じです。

iex(1)> Task.start_link(fn -> IO.puts("Something done!") end)
Something done!
{:ok, #PID<0.86.0>}

【2022/10/23】Task.async を使った負荷試験ツールを見つけました
Elixirでパラレルな負荷試験ツールを作る

2.Agent

 Agentはstateを持ったバックグラウンドのプロセスです。stateは、あるプロセスやnode内のいろいろな場所からアクセスされます。他のnodeからアクセスされることもあります。ザックリ言えばGenServerをお手軽に使うイメージですかね。

 (補足)ここで述べるのは基本的な事柄ですので必要に応じて「Agent - Hexdocs」を参照してください。

 簡単な使い方です。

# iex
iex(1)> {:ok, pid} = Agent.start_link(fn -> %{name: "Yamada", age: 30} end)
{:ok, #PID<0.86.0>}
iex(2)> Agent.get(pid, fn state -> state.name end)
"Yamada"
iex(3)> Agent.update(pid, fn state -> %{state | age: state.age + 1} end)
:ok
iex(4)> Agent.get(pid, fn state -> state end)
%{age: 31, name: "Yamada"}

今回は以上です。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1