Techioz Blog

Ruby Async Gem: 基本的な使用例は何ですか?

概要

Ruby 3.0 では、非同期 gem が標準ライブラリ関数のブロック IO と互換性があるため、基本的な機能を理解したかったのですが、簡単な例を見てすでに混乱しています。

require 'async'

n = 10
n.times do |i|
  Async do
    HTTParty.get("https://httpbin.org/delay/1.6")
  end
end

これは並列性を示しません。 Gem の Kernel#async に関するドキュメントを見ると、次のように書かれています。

しかし、プロジェクトのドキュメントではそれが明確になっているようです。

上記の例を機能させるには:

require 'async'

n = 10
Async do
  n.times do |i|
    Async do
      HTTParty.get("https://httpbin.org/delay/1.6")
    end
  end
end

これは機能しますが、読者にとってはわかりにくいようです。最初の Async がブロックしているのに他の Async はブロックしていないことを読者としてどうやって知ることができるでしょうか?

したがって、質問は次のとおりです。async gem の正規の基本的な使用法は何ですか?

参考文献:

解決策

質問が「async gem の基本的な例は何ですか」である場合、2 番目のコード スニペットがその 1 つです。

「なぜ API はこのようになっていますか?」という質問がある場合は、問題掲示板でこの gem に取り組んでいる開発者に質問する必要があるでしょう。

「Async::Reactor の実行 (これが Kernel#Async の動作です) がブロックされるかどうかをどうやって確認できますか?」という質問であれば、Async#Task.current を呼び出すことができます。

require 'async'
require 'net/http'

def log_whether_blocking
  if Async::Task.current?
    puts "Async::Task#current? is truthy, calling `Kernel#Async` will not block"
  else
    puts "Async::Task#current? is falsey, calling `Kernel#Async` will block"
  end
end

n = 10
Async do
  n.times do |i|
    log_whether_blocking
    Async do
      Net::HTTP.get(URI("https://httpbin.org/delay/1.6"))
    end
  end
end

n.times do |i|
  log_whether_blocking
  Async do
    Net::HTTP.get(URI("https://httpbin.org/delay/1.6"))
  end
end

これにより出力が得られます

Async::Task#current? is truthy, calling `Kernel#Async` will not block
Async::Task#current? is truthy, calling `Kernel#Async` will not block
Async::Task#current? is truthy, calling `Kernel#Async` will not block
Async::Task#current? is truthy, calling `Kernel#Async` will not block
Async::Task#current? is truthy, calling `Kernel#Async` will not block
Async::Task#current? is truthy, calling `Kernel#Async` will not block
Async::Task#current? is truthy, calling `Kernel#Async` will not block
Async::Task#current? is truthy, calling `Kernel#Async` will not block
Async::Task#current? is truthy, calling `Kernel#Async` will not block
Async::Task#current? is truthy, calling `Kernel#Async` will not block
Async::Task#current? is falsey, calling `Kernel#Async` will block
Async::Task#current? is falsey, calling `Kernel#Async` will block
Async::Task#current? is falsey, calling `Kernel#Async` will block
Async::Task#current? is falsey, calling `Kernel#Async` will block
Async::Task#current? is falsey, calling `Kernel#Async` will block
Async::Task#current? is falsey, calling `Kernel#Async` will block
Async::Task#current? is falsey, calling `Kernel#Async` will block
Async::Task#current? is falsey, calling `Kernel#Async` will block
Async::Task#current? is falsey, calling `Kernel#Async` will block
Async::Task#current? is falsey, calling `Kernel#Async` will block

API に詳しくない人にとっては便利な機能かもしれませんが、Kernel#Async 呼び出しでラップすると、一連の Async::Reactor の実行を常に確実に同時に実行できます。ただし、場合によっては不要な場合もあります。そうするために。