関連記事
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。
標準入力からパイプで流したデータをキャンセル可能にしたいなーって思って、ライブラリ探してたら以下を発見。
Linuxの場合は、内部で epoll を使って制御してくれているみたいですね。ちょっと試してみました。
以下、サンプルです。
サンプル
package main import ( "errors" "fmt" "log" "os" "time" "github.com/muesli/cancelreader" ) func main() { log.SetFlags(log.Lmicroseconds) log.SetOutput(os.Stdout) if err := run(); err != nil { log.Panic(err) } } func run() error { // // 標準入力をキャンセル可能なReaderにする // // cancelreader.CancelReaderは、Linuxの場合 // 内部で epoll を利用して制御を行っている。 // // なので、非ブロッキングI/Oが可能なファイルディスクリプタに対してのみ // キャンセル可能となっている。非ブロッキングI/Oが可能なものは例えば以下のもの。 // - ソケット // - パイプ // - FIFO // // 通常のファイルは非ブロッキングI/O可能なファイルディスクリプタでは無いことに注意。 // 通常のファイルをepollで利用すると epoll_ctl() で EPERM が返ってくる。 // 通常のファイルをキャンセル可能にしたい場合は io_uring などを検討する。 // var ( reader cancelreader.CancelReader err error ) reader, err = cancelreader.NewReader(os.Stdin) if err != nil { return fmt.Errorf("cancelreader.NewReader() failed: %w", err) } defer reader.Close() // // 3秒後にキャンセル // go func() { time.Sleep(3 * time.Second) reader.Cancel() }() // // 500ms毎に1文字読み込み // var ( buf [1]byte ) for { clear(buf[:]) if _, err = reader.Read(buf[:]); err != nil { if errors.Is(err, cancelreader.ErrCanceled) { log.Println("CANCELED") break } return fmt.Errorf("reader.Read() failed: %w", err) } log.Print(string(buf[:])) time.Sleep(500 * time.Millisecond) } return nil }
試してみます。
$ task task: [default] echo -n helloworld | go run main.go 15:09:24.486356 h 15:09:24.987136 e 15:09:25.487775 l 15:09:25.988534 l 15:09:26.489191 o 15:09:26.989851 w 15:09:27.490367 CANCELED
ちゃんと途中でキャンセルがかかっていますね。
参考情報
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。