oinume journal

Scratchpad of what I learned

Intellij IDEAを2019.2にアップグレードするとコピペや入力がおかしくなる問題のワークアラウンド

先日IntelliJ IDEAを2019.2にアップグレードしたところ、以下の問題に遭遇した。

  • エディタでのコピー&ペーストすると同じものが2回ペーストされる
  • 文字の入力に取りこぼしがある(publicって入力してもpubcみたいなる)

バグトラッカーを見たところ、Copied text is pasted twice via Cmd+V (with Japanese input)が不具合として上がっていたが修正されそうな気配がなくて途方にくれていたところ、こんなワークアラウンドがあることをTwitter上で発見したので試してみた。

JBR11というJava Runtimeが原因とのことなので、これを古いバージョンにすればよいらしい。

自分は jbrx-8u202 をわざわざダウンロードするのが面倒だったので

$ brew cask install homebrew/cask-versions/adoptopenjdk8

してから

$ /usr/libexec/java_home -v "1.8"
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home

で出力されたディレクトリをSwitch Boot JDKで指定したところ問題が起きなくなった。めでたしめでたし。

IntelliJ IDEAハンズオン――基本操作からプロジェクト管理までマスター

IntelliJ IDEAハンズオン――基本操作からプロジェクト管理までマスター

MacBookAir 2019を買おうか悩み中

今使っているのはMacBookPro 13inch late 2016 Two Thunderbolt3 portsなんだけど、2018年モデルのMacBookAirにCPUスコアは負けている...

browser.geekbench.com

Single-Core Score: 3886
Multi-Core Score: 7701

以下は2018年モデルのGeekBenchのスコア。

browser.geekbench.com

Single-Core Score: 4008
Multi-Core Score: 7396

Touch IDがついていてTouch Barがないモデルだし、重さも100gぐらい軽いし買い替えようか検討中...

2019年6月の振り返り

2019年がもう半分が過ぎてしまった。やばい。

アウトプット

6月は頑張ってブログを書いた。それなりの分量の記事を書こうとすると週に1個が限界な気がするので、良いペースだと思う。

睡眠

1日6時間以上寝る、ということを今年の目標にしている。ヘルスケアアプリによると6月は平均6時間13分だった。ただ、毎日6時間以上の睡眠ができているわけではないので、これは今後の課題である。

業務で必要なものを身につける

これは以下の3つに絞る。これ以外は必要になったら勉強する。

  • Go
  • gRPC
  • SQLを復習する

Goについては、Go言語による並行処理という本を読んでいるけどとてもわかりやすくてよい。

Go言語による並行処理

Go言語による並行処理

  • 作者: Katherine Cox-Buday,山口能迪
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2018/10/26
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る

英語

もともとの目標はIELTS 6.5だが、今の自分のレベルだと高すぎるのでOverall 6.0に下方修正する。そして勉強は相変わらずできていない。電車の中の隙間時間でなるべくやるのが良いのだろうけど、Goの本を読んだりするほうが楽しいのでそっちをやってしまっている。

アルゴリズム

特に進捗なし

まとめ

6月は仕事がかなり落ち着いてきたのでプライベートの勉強がかなり捗った。来月もこれぐらいのペースで頑張って行きたい。

Goのcontextによるキャンセルやタイムアウト

これはなに?

Go言語におけるcontextパッケージを使ったキャンセルやタイムアウトについて説明する。この記事を読むと以下について詳しくなれるはず...!

  • context.WithCancel
  • context.WithTimeout
  • context.Done
  • context.Err

とはいいつつも、かなり自分向けのまとめではあるし既出のトピックなので以下の記事を読むともっとわかりやすいはず。

done channelを使ったキャンセルの実装

contextがあると何が嬉しいのかを説明するために、まずはdone channelを使ってキャンセルを行うコードを書いてみる。これはGo言語による並行処理のP.137に記載されていたサンプルプログラムを少し修正したものである。

このプログラムでやりたいことは、close(done)によってprintGreeting, printFarewell, genGreeting, genFarewell, localeの関数をキャンセルしたい、ということである。

このプログラムを実行すると以下の出力が得られる(場合によっては先にhello world!が出力されるかもしれない)。

$ go run context/done_chan/main.go
goodbye world!
hello world!

実行の流れとしては以下のようになっている(この図もGo言語による並行処理から引用)。

この図をより詳細に説明すると

  • main関数からprintGreetingとprintFarewellをgoroutineを起動して呼び出し、sync.WaitGroup.Waitを使って待つようにしている(L38)
  • printGreetingはgenGreeting -> locale の順番で関数を呼び出す
    • localeは select の中で <-time.After(5 * time.Second) で5秒待ち、EN/USを返す
    • genGreetingはlocale()の呼び出しがEN/USの場合 hello を返す
    • printGreetingはgenGreetingの戻り値を出力する
  • printFarewellもgenFarewell -> locale の順番で関数を呼び出す
    • localeは select の中で <-time.After(5 * time.Second) で5秒待ち、EN/USを返す
    • genFarewellはlocale()の呼び出しがEN/USの場合 goodbye を返す
    • printFarewellはgenGreetingの戻り値を出力する
  • printGreeting, printFarewellのgoroutineが終了したので、main関数は処理を抜ける。つまり defer close(done) が実行される(L12)

では、以下のように defer close(done) の部分を、close(done)した後にprintGreetingを呼び出すようにしたらどうなるだろうか?

defer func() {
    close(done)
    if err := printGreeting(done); err != nil {
        fmt.Printf("err=%v\n", err)
    }
}()

上のように修正して再度プログラムを実行すると、以下のように出力される。

$ go run context/done_chan/main.go
goodbye world!
hello world!
err = canceled

canceledがprintGreetingのerrorとして返ってきた。このerrorはlocale関数の以下の部分で生成されたもので、done channelがすでにcloseされたため、selectのこのcaseに処理が来たということである。

select {
case <-done:
    return "", fmt.Errorf("canceled")
    ...
}

このようにprintGreetingやprintFarewellをchannelをcloseすることでキャンセルを実現することができた。それでは次に、contextパッケージを使ったキャンセルのやり方を見てみよう。

contextを使ったキャンセルの実装

それでは、さきほどのdone channelの例をcontextを使って実装し直してみよう。

done channelの実装と比べた場合のポイントとしては以下である。

  • context.Background でroot contextを作成
  • context.WithCancel(ctx) では戻り値として子のcontextとキャンセルするための関数が返ってくる
  • このキャンセルするための関数 cancel を呼び出すことでキャンセル処理を実行する
  • printGreetingなどの関数の引数に ctx を渡す。これによって末端のlocaleまでctxを引き回している

このプログラムを実行してみると、以下のような出力になりcontextを使ってキャンセルが実装できたことが確認できた。これがcontextパッケージの大きな機能の一つである。

$ go run context/with_cancel/main.go
goodbye world!
hello world!
err = context canceled
  • ctx.Doneはcontextがキャンセルされるまでブロックされる受信専用のchannelを返す。このchannelからデータを取得できたということはキャンセルされたということなので、ctx.Err() でエラーを返すようにしている
  • context canceled というエラーはlocale関数のctx.Err() の戻り値である。contextパッケージにはCanceledというグローバル変数が定義されており、これが返却されている

タイムアウト

一番最初のdone channelのプログラムでは単純にprintGreetingやprintFarewellなどの関数の実行をキャンセルしたいタイミングでclose(done)しているだけだが、例えばprintGreetingやprintFarewell(およびこれらが呼び出す関数)の呼び出しを、一定時間が経過した場合にキャンセルしたいと思った場合はどうすればよいだろうか? context.WithTimeoutを使うことでそのような実装が簡単にできる。

  • genGreetingでcontext.WithTimeoutで1秒後にタイムアウトするようにして新しい子のcontextを生成する。そしてlocaleにそれを渡し、 defer cancel() する
  • main関数のprintGreetingを呼び出すgoroutineで、printGreetingが失敗したら他の関数呼び出しをキャンセルするためにcancelを呼び出す

このプログラムの実行結果は以下のようになる。

$ go run ./context/with_timeout/main.go
cannot print greeting: context deadline exceeded
cannot print farewell: context canceled

まとめ

contextを使うことでキャンセルやタイムアウトが簡単に実装できることを説明した。この内容については以下のGo言語による並行処理にすべて書かれているし、よりgoroutineや並行処理について知りたい場合はこの本を読んでみることを強くオススメする。

Go言語による並行処理

Go言語による並行処理

Amazon