errgroup突然ですがx/sync/errgroupはとても便利です。並行で外部リソースにアクセスしたいときなどはsync/WaitGroupで書いてエラーの扱いを自分で頑張るよりerrgroupに任せた方が楽です。doSomething(err)}errgroupの振る舞いについてそんな便利なerrgroupなのですが。同期的な疑似コードを書いてみると以下の様な振る舞いをするものとみなすことができます。やってることは順に実行してエラーが発生した時点で処理を中断するというような形式のものです。errgroupの亜種ちょっとだけ発想を変えてみましょう。先程のerrgroupによる処理を読み込むと実行される列のようなもので考えてみます。ここでf0,f1,...はfsの要素すると成功したら(失敗しなかったら)処理を継続する(and)処理と見ることができます。個人的にはこの見た目が論理演算のandと似たような形のように見えました。andがあるならorについても考えることができそうです。ここでf0,f1,...はfsの要素orによる連鎖は、たとえ失敗したとしても次の処理を実行していくものと考えることにします(一度trueになったら短絡して処理を打ち切りということは考えない)。すると今度は以下の様な疑似コードを模した構造のものが考えられそうです。ここでerrsに対して何か処理を行うそうです。error値がたとえあったとしても処理を継続しつつerror値はどこかに格納しておくみたいな処理になっています。格納する場所はどのような形式でも良いのですが。上の疑似コード上ではsliceということにしています。いった構造を持ったヘルパーをerrgroupの亜種として考えることはできるでしょうか?というのが今回の話です。?名前は特に良いものが思いつかなかったのでテキトウにConditionalとか付けて作ってみます。errgroupのGroupと対応づけるとすると以下の様なインターフェイスを持っているはずです。error}ここでちょっと考えてみると、Wait()は違いそうです。エラーが複数存在しているはずなので。使用しているときの状況も考えてみたのですが。error値を取得したらすぐにその場で消化したいという気持ちになりました(例えば100個くらいタスクが存在してその5個目のタスクで発生したエラーを全部のタスクが終わってからみたいでしょうか?ということなどを考えたら発生したエラーは発生したタイミングでハンドリングしたいと思いました)。iterator発生したerror値をその場で使いたいと来たらchannelです。実際channelを一種のiteratorと見做す使いかたはたまに存在するデザインパターンの一種だと思います。3内部的にはスリープしたりなどの待つ処理が入るのですが。channelをrangeでforループすると他の言語のイテレータ(ジェネレータ)のように扱うことができます。ちなみに複数のchannelを同時に消費したい場合には、forループの部分で複数このgoroutineを回すだとか。rangeによるforループを諦めるといったことをする必要があります。}()}wg.Wait()複数のproducerを消費する例。}}()ちなみに複数のproducerを1つのworkerで消費することもできるのですが。終了条件の扱いなどでちょっと大変になります。実際の所rangeでのchannelのループは以下のような記述の省略形です。}}このselectでのokの部分はcloseされたchannelでfalseの値がかえってきます。forループでchannelを使うにはcloseが必須ということになります。してこのcloseの必要性の話まで丁寧に書いてきたかと言うと後々必要になるからです。インターフェイスについて考えるchannelをイテレータと見做すという話を含めると、以下の様なインターフェイスの方が妥当かもしれません。error}実はこれではだめです。理由は単純で使用するときコードは以下の様になるのですが。goのruntimeは全てのgoroutineが止っていることを許しません。f0,f1でsleepのような処理が止まる処理が存在してしまった場合に、c.Wait()部分のmainの処理も止まってしまうのでだめです。deadlock!そんなわけでちょっとだけ変更した以下のようなインターフェイスにします。error))}使いかたはこうです。以下のコードは、時折2で割り切れる数の場合にエラーが発生するのですが。その発生したエラーは発生したタイミングですぐにログに出力されて欲しいと言うようなもの例です。}})fmt.Println("done")失敗した場合も即座にエラーが出力されてますね(start..endが終わってからerrorが来ていない)。940done(このコードだけなら直接エラー値を返さずgoroutineでループしている処理の中でエラーの出力をすれば良いのでは?という話があったりもしますが。実際のコードではエラー処理が共通で行われていたりなどあると思います)。実装は以下の様な感じです。?並行実行の数を制限したい場合には、semaphoreを使うと便利です。golang.org/x/sync/semaphoreこんな感じに。time.Millisecond)例えば2個ずつまでしか実行しない場合。437doneキャンセル処理は?キャンセル処理はどうしましょう?今回の実装では対応していません。あったほうが良いですか?contextを見るというのもありだとおもいます。以下みたいな処理をGoの中などで書いてみるイメージです。something}あるいはGo()メソッドの実装を以下の様にしてみるだとか。}}使う時にctxを渡すように変わります。}})正直な所キャンセルの対応はあまり良い感じではない気がしています。23:59x/sync/errgroupの亜種みたいなものを考えたりしていたTweet広告を非表示にする関連記事2019-03-19N個のchannelがcloseされるまで読み込む方法についてN個のchannelがcloseされるまで読み込む方法についてのメモ。標準のlogパッケージの使いかたをちょっと変えるだけで便利な形...2018-04-28goでは自分自身を返すメソッドは定義しないほうが良いのでは?という話。最近色々コードを書いていた中で、goでは自分自身を返すメソッ...2018-04-08go言語でASTの解析にgo/typesの機能を使うことの威力についてgoはASTの解析が手軽なのが便利。(個人的なコード生成のための環境を作っている。componentという機能を追加した月別アーカイブはてなブログをはじめよう!podhmoさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。