2020/09/16


Go のアプリケーションを作っていると、シグナルの受信に伴い処理を中断したり再起動する処理を実装する事が多い。これまでは signal.Notify でシグナルをキャッチし、別途 context.WithCancel で作成したコンテキストを自ら cancel する処理を書かなければならなかった。Go の tip に入ったコミットにより、これが幾分改善される様になった。

package main

import (
    "context"
    "fmt"
    "os"
    "os/signal"
    "time"
)

func main() {
    ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
    defer stop()

    select {
    case <-time.After(time.Second):
        fmt.Println("done")
    case <-ctx.Done():
        stop()
        fmt.Println("canceled")
    }
}

このコードを実行すると、1秒経過すると done が、途中で CTRL-C をタイプすると canceled が表示される。一見、利用用途が少ない様に見えるが以下の様に goroutine を複数起動し、signal で一括終了する時には便利。

package main

import (
    "context"
    "fmt"
    "os"
    "os/signal"
    "sync"
)

func blocking(ctx context.Context, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("worker started")
    <-ctx.Done()
    fmt.Println("worker canceled")
}

func main() {
    ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
    defer stop()

    var wg sync.WaitGroup
    wg.Add(3)
    go blocking(ctx, &wg)
    go blocking(ctx, &wg)
    go blocking(ctx, &wg)

    wg.Wait()
}
改訂2版 みんなのGo言語 改訂2版 みんなのGo言語
松木 雅幸, mattn, 藤原 俊一郎, 中島 大一, 上田 拓也, 牧 大輔, 鈴木 健太
技術評論社 Kindle版 / ¥2,278 (2019年08月01日)
 
発送可能時間:

Posted at by



2020/06/19


元ネタ

作ったもの。

mattn/fakemovie · GitHub
https://github.com/mattn/fakemovie

Go のパッケージにしてあるので、Go の HTTP サーバからサーブする画像全てに再生ボタンを付ける様なミドルウェアを作る事もできてとても便利です。一応、オマケとしてコマンドを用意してあります。

$ fakemovie -r 40 input.png
fakemovie

こんな感じに使えるのでご利用下さい。

Posted at by



2020/04/05


1週間ほど前の深夜、ふと Go で連結リスト構造を書いていたら次第に car/cdr 形式になってしまい、気付いたら手が滑って Lisp 処理系を作り始めてしまいました。

初日は深夜だったのでパーサを書いた所で終了。次の日の夕方には四則演算と FizzBuzz が動きました。実は Lisp 処理系を書くのは人生でたぶん4回目くらいで、前回はC言語で書きました。

GitHub - mattn/cisp: Minimal Lisp Interpreter
https://github.com/mattn/cisp

今回のルールとして「過去の自分の実装や他の実装は見ない」というオレオレルールを作ってしまったので幾分時間が掛かってしまった様に思います。テストコードはさすがにいいだろという事で、cisp のテストコードは借りました。マクロを除いて cisp と互換性があります。

今回 Lisp 処理系を書きながらなんとなくやってみたいなと思っていたのが Lisp での非同期処理。Go言語の goroutine と channel を使って通信出来たらどんな物ができるだろう、という研究目的でした。

本日ようやく goroutine/channel が動いたのでブログで公開しておきます。

GitHub - mattn/golisp: Lisp Interpreter
https://github.com/mattn/golisp

(go:make-chan string) で chan を作る事ができ、(go:chan-send ch "foo") で送信、(go:chan-recv ch) で受信する事ができます。また (go (print 1) (print 2)) で goroutine が起動するので、以下の様な channel を使った通信ができます。

(setq time (go:import time))
(let ((ch (go:make-chan string 1)))
    (go
        (.Sleep time 1e9)
        (go:chan-send ch "1")
        (.Sleep time 1e9)
        (go:chan-send ch "2")
        (.Sleep time 1e9)
        (go:chan-send ch "3")
        (.Sleep time 1e9)
        (go:chan-send ch "ダーッ!")
    )
    (print (car (go:chan-recv ch)))
    (print (car (go:chan-recv ch)))
    (print (car (go:chan-recv ch)))
    (print (car (go:chan-recv ch)))
)

研究の成果としては、面白い動きが確認できたのでまずまずの成果と思います。上記の様に Go のパッケージを import して関数やメソッドを呼び出せる様になっています。例えば乱数を表示するには以下の様に実行します。

(setq time (go:import 'time))
(setq rand (go:import 'math/rand))
(.Seed rand (.UnixNano (.Now time)))
(print (.Int rand))

今の所、実用的ではありませんが、細々とメンテナンスしていってツールを書く程度に使えるまでは持っていきたいと思います。

Posted at by