當前位置: 首頁>>代碼示例 >>用法及示例精選 >>正文


Elixir Task.async_stream用法及代碼示例


Elixir語言中 Task.async_stream 相關用法介紹如下。

用法一

async_stream(enumerable, fun, options \\ [])
(從 1.4.0 開始)
@spec async_stream(Enumerable.t(), (term() -> term()), keyword()) :: Enumerable.t()

返回在 enumerable 中的每個元素上同時運行給定函數 fun 的流。

async_stream/5 工作方式相同,但使用匿名函數而不是 module-function-arguments 元組。 fun 必須是 one-arity 匿名函數。

每個 enumerable 元素作為參數傳遞給給定的函數 fun 並由其自己的任務處理。任務將鏈接到調用者進程,類似於 async/1

示例

異步計算每個字符串中的代碼點,然後使用 reduce 將計數相加。

iex> strings = ["long string", "longer string", "there are many of these"]
iex> stream = Task.async_stream(strings, fn text -> text |> String.codepoints() |> Enum.count() end)
iex> Enum.reduce(stream, 0, fn {:ok, num}, acc -> num + acc end)
47

有關討論、選項和更多示例,請參閱 async_stream/5

用法二

async_stream(enumerable, module, function_name, args, options \\ [])
(從 1.4.0 開始)
@spec async_stream(Enumerable.t(), module(), atom(), [term()], keyword()) ::
  Enumerable.t()

返回一個流,其中給定函數(modulefunction_name)同時映射到 enumerable 中的每個元素上。

enumerable 的每個元素都將附加到給定的 args 並由其自己的任務處理。這些任務將鏈接到一個中間流程,然後該流程鏈接到調用者流程。這意味著任務中的失敗會終止調用者進程,而調用者進程中的失敗會終止所有任務。

流式傳輸時,每個任務將在成功完成時發出{:ok, value},如果調用者正在捕獲退出,則發出{:exit, reason}。結果的順序取決於:ordered 選項的值。

並發級別和允許任務運行的時間可以通過選項來控製(參見下麵的"Options" 部分)。

考慮使用 Task.Supervisor.async_stream/6 在主管下啟動任務。如果您發現自己捕獲出口以確保任務中的錯誤不會終止調用者進程,請考慮使用 Task.Supervisor.async_stream_nolink/6 來啟動未鏈接到調用者進程的任務。

選項

  • :max_concurrency - 設置同時運行的最大任務數。默認為 System.schedulers_online/0

  • :ordered - 結果是否應以與輸入流相同的順序返回。當輸出被排序時,Elixir 可能需要緩衝結果以按原始順序發出它們。將此選項設置為 false 將禁用以刪除排序為代價進行緩衝的需要。當您僅將任務用於副作用時,這也很有用。請注意,無論:ordered 設置為什麽,任務都將異步處理。如果您需要按順序處理元素,請考慮改用 Enum.map/2 Enum.each/2 。默認為 true

  • :timeout - 每個任務允許執行的最長時間(以毫秒或 :infinity 為單位)。默認為 5000

  • :on_timeout - 任務超時時該怎麽做。可能的值是:

    • :exit(默認)- 調用者(產生任務的進程)退出。
    • :kill_task - 超時的任務被殺死。為該任務發出的值是 {:exit, :timeout}

示例

讓我們構建一個流,然後枚舉它:

stream = Task.async_stream(collection, Mod, :expensive_fun, [])
Enum.to_list(stream)

可以使用:max_concurrency 選項增加或減少並發性。例如,如果任務 IO 很重,則可以增加該值:

max_concurrency = System.schedulers_online() * 2
stream = Task.async_stream(collection, Mod, :expensive_fun, [], max_concurrency: max_concurrency)
Enum.to_list(stream)

如果您不關心計算結果,可以使用 Stream.run/1 運行流。還要設置 ordered: false ,因為您也不關心結果的順序:

stream = Task.async_stream(collection, Mod, :expensive_fun, [], ordered: false)
Stream.run(stream)

第一個要完成的異步任務

也可以使用 async_stream/3 執行M個任務,找到N個任務完成。例如:

[
  &heavy_call_1/0,
  &heavy_call_2/0,
  &heavy_call_3/0
]
|> Task.async_stream(fn fun -> fun.() end, ordered: false, max_concurrency: 3)
|> Stream.filter(&match?({:ok, _}, &1))
|> Enum.take(2)

在上麵的示例中,我們正在執行三個任務並等待前兩個任務完成。我們使用 Stream.filter/2 將自己限製為僅成功完成的任務,然後使用 Enum.take/2 檢索N 個項目。請注意,設置 ordered: falsemax_concurrency: M 很重要,其中 M 是任務數,以確保所有調用同時執行。

注意:未綁定 async + take

如果您想潛在地處理大量項目並僅保留部分結果,則可以end-up 處理比預期更多的項目。讓我們看一個例子:

1..100
|> Task.async_stream(fn i ->
  Process.sleep(100)
  IO.puts(to_string(i))
end)
|> Enum.take(10)

在具有 8 個內核的機器上運行上麵的示例將處理 16 個項目,即使您隻需要 10 個元素,因為 async_stream/3 同時處理項目。那是因為它將一次處理 8 個元素。然後所有 8 個元素大致同時完成,導致 8 個元素被啟動以進行處理。在這額外的 8 個中,隻有 2 個將被使用,其餘的將被終止。

根據問題,您可以預先過濾或限製元素的數量:

1..100
|> Stream.take(10)
|> Task.async_stream(fn i ->
  Process.sleep(100)
  IO.puts(to_string(i))
end)
|> Enum.to_list()

在其他情況下,您可能需要調整 :max_concurrency 以限製有多少元素可能被過度處理,但會降低並發性。您還可以將要采用的元素數設置為 :max_concurrency 的倍數。例如,在上麵的示例中設置max_concurrency: 5

相關用法


注:本文由純淨天空篩選整理自elixir-lang.org大神的英文原創作品 Task.async_stream(enumerable, fun, options \\ [])。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。