読者です 読者をやめる 読者になる 読者になる

write ahead log

ロールフォワード用

golangで実行中のOSの種類を判別する

簡単だった.ポータビリティがすごい.

package main

import (
    "fmt"
    "runtime"
)

func main() {
    switch runtime.GOOS {
    case "windows":
        fmt.Println("running on Windows.")
    case "darwing":
        fmt.Println("running on Mac OSX.")
    case "linux":
        fmt.Println("running on Linux.")
    case "freebsd":
        fmt.Println("running on BSD.")
    default:
        fmt.Println("running on Other OS.")
    }
}

[実行結果]

$ ./sample.exe
running on Windows.

くらいあればとりあえずは事足りる気がする. (調べてないけど, Plan9とかも対応してそうだよね….)

参考

The Go Programming Language - Package runtime

vimrcを変えた

golangを良く書くようになってきたので, gofmtを保存時に自動実行するようにした.

あとはMarkdownでfolding出来ると便利とようやく気付いたのでfoldingを足した.
(今まで使ってなかったんですよね…)

プラグインはホントわけわからなくなりそうなので極力使いたくない.
(標準でも十分すぎるぐらいvimは機能多いし…)

とはいえ100行超えてきたのでそろそろこの姿勢も見直さないとダメだろうか…

gist.github.com

参考

Stack overflow - Vim Markdown Folding?

UbuntuでUpstartを使ってプログラムをデーモン化する

golangで簡単なHTTPサーバを作ってこれをデーモン化してみましょう.

今後はSystemdが主流に代わっていくのでしょうが, まだまだUpstartも多いと思います.

1. サンプルプログラムの準備

サンプルプログラムは以下のような簡単なものです.

Helloを挨拶してくれるだけです.

package main

import (
        "net/http"
        "fmt"
)

func main() {
        http.HandleFunc("/", helloHandler)
        http.ListenAndServe(":8080", nil)
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello\n")
}

[実行結果]

vagrant@vagrant-ubuntu-trusty-64:~/hello$ ./hello &
[1] 2584
vagrant@vagrant-ubuntu-trusty-64:~/hello$ curl localhost:8080
Hello

2. ランレベルのおさらい

*nixにはランレベルという概念があります.

デーモン化にあたって必要な知識になるのでここでおさらいしておきます.

参考: wikipedia - ランレベル

ランレベル/ID 名称 説明
0 停止 システムの停止、またはシャットダウン
1 シングルユーザモード デーモン起動なし.rootのみ利用可.いわゆるセーフモード
2 マルチユーザモード ネットワークが使えない.
3 マルチユーザモード(通常) いわゆる通常のモード.X11はなし
4 未使用
5 X11 ランレベル3 + Xウィンドウ
6 再起動 システム再起動

3. 起動スクリプトの準備

起動スクリプトを以下に作成します.

/etc/init/

今回は以下のようにしましょう.

vi /etc/init/hello-svr.conf

内容は以下のとおりです.

description "Hello Server"
author "twinbird<mail@hostname.domain.jp>"

start on runlevel [235]
stop on runlevel [016]

chdir /usr/local/bin/hello-svr
exec ./hello
respawn

簡単な解説を付けると

  • description => 説明
  • author => 記述者とメアド
  • start on runlevel[235] => ランレベル2,3,5で開始
  • stop on runlevel[016] => ランレベル0,1,6で停止
  • chdir => ディレクトリを移動
  • exec => プログラムを実行
  • respawn => プロセスが死んだら再度実行する

4. 設定を認識させる

# initctl reload-configuration
# initctl list

以下のようにリストが表示されます. ちゃんと出てますね.

...[中略]...
tty6 start/running, process 968
dmesg stop/waiting
hello-svr stop/waiting
...[中略]...

5. 操作する

操作コマンドinitctlを使うと色々できます.

ちなみにサービスの事をjobと呼ぶみたいです.
(サービスだとWindows用語になるのかな.デーモンと呼ぶのが一般的?)

コマンド 説明
initctl status [job名] jobの状態確認
initctl list jobと状態のリストを表示
initctl start [job名] jobの起動
initctl stop [job名] jobの停止
initctl restart [job名] jobの再起動
initctl reload [job名] jobへsighupシグナルを送る
initctl reload-configuration 設定ファイルをリロード
1. 起動する

簡単です.

root@vagrant-ubuntu-trusty-64:~# initctl start hello-svr
hello-svr start/running, process 1617

確認はcurlで.

root@vagrant-ubuntu-trusty-64:~# curl localhost:8080
Hello
2. 停止する

こちらも簡単.

root@vagrant-ubuntu-trusty-64:~# initctl stop hello-svr
hello-svr stop/waiting

確認.

root@vagrant-ubuntu-trusty-64:~# curl localhost:8080
curl: (7) Failed to connect to localhost port 8080: Connection refused

6. 起動スクリプトをもうちょっと調べる

メモしておきます.

script ~ end script

上記の例ではexecで直接バイナリを起動しましたが, script ~ end scriptの ブロックで囲むとその間を実行するようです.

script
    # comment
    if [...]; then
        ...
    fi
end script

ちょっとしたシェルスクリプト書くならこの方がいいですね.

respawnに試行回数やインターバルを付ける

respawnはジョブが死んだら再度実行してくれる便利な記述ですが, 再試行するのに回数制限を付けたいパターンもあるかもしれないですね.

respawn limit 最大再試行回数 インターバル時間(秒)

で実現できます.

job起動・停止前後で何かする
  • pre-start
  • post-start
  • pre-stop
  • post-stop

という命令で指定できます.

pre-start exec [何かコマンド]

割と簡単にデーモン化できますね.

次はSystemdを使ってみたいです.

7. 参考

Getting Started - upstart

インフラエンジニアway - Upstartを使ってお手軽deamon化

偏った言語信者の垂れ流し - Upstartを使う

golangで画像を扱う(imageパッケージを使う)

書店でInterface 5月号を見つけて, 画像処理が懐かしくなったので.
(つい買ってしまった.他にも車載用OSとか面白いしね.)

golangにもimageパッケージという2D画像用ライブラリが標準で備わってるようなのでちょっと使ってみる.

1. 概要

golangの他の標準パッケージと同様, このパッケージも最低限の機能しか用意されていないみたいです.

The Go Programming Language - Package image

サブディレクトリ含めてもそんなにない

ざっと見た感じだと, 以下くらいの機能なのかな?

  • アルファ値
  • 色空間や濃淡(グレースケール)
    • RGB
    • CMY
    • Gray
  • 矩形
  • カラーパレット(pallete)
  • 上記の組み合わせ(draw)
  • 画像フォーマットのデコード処理(gif/png/jpeg)

Uniformって何.まっさらなとこからやるときに使うのか?

まぁいいや.

あと, 現時点だと扱える画像フォーマットは

だそうです.

2. 何か書いてみる

とにかく使ってみましょう. 簡単なグラフィックから.

2.1. 箱を描く

簡単そうなのから.

NewRGBAでRGB形式の画像を作成できます.

package main

import (
    "os"
    "image"
    "image/jpeg"
)

func main() {
    x := 0
    y := 0
    width := 100
    height := 50

    // RectからRGBAを作る(ゼロ値なので黒なはず)
    img := image.NewRGBA(image.Rect(x, y, width, height))

    // 出力用ファイル作成(エラー処理は略)
    file, _ := os.Create("sample.jpg")
    defer file.Close()

    // JPEGで出力(100%品質)
    if err := jpeg.Encode(file, img, &jpeg.Options{100}); err != nil {
        panic(err)
    }
}

[実行結果]

f:id:twinbird_htn:20170423001840j:plain

2.2. 箱を描く(色指定)

一歩前へ.

Setでピクセル単位に色をセットしていくだけです.

画像サイズはimg.Rectの中にあるX, Yで取得できます.

package main

import (
    "os"
    "image"
    "image/jpeg"
    "image/color"
)

// 画像を単色に染める
func fillRect(img *image.RGBA, col color.Color) {
    // 矩形を取得
    rect := img.Rect

    // 全部埋める
    for h := rect.Min.Y; h < rect.Max.Y; h++ {
        for v := rect.Min.X; v < rect.Max.X; v++ {
            img.Set(v, h, col)
        }
    }
}

func main() {
    x := 0
    y := 0
    width := 100
    height := 50

    // RectからRGBAを作る(ゼロ値なので黒なはず)
    img := image.NewRGBA(image.Rect(x, y, width, height))
    // 赤色に染める(透過なし)
    fillRect(img, color.RGBA{255, 0, 0, 0})

    // 出力用ファイル作成(エラー処理は略)
    file, _ := os.Create("sample.jpg")
    defer file.Close()

    // JPEGで出力(100%品質)
    if err := jpeg.Encode(file, img, &jpeg.Options{100}); err != nil {
        panic(err)
    }
}

[実行結果]

f:id:twinbird_htn:20170423001904j:plain

2.3. 箱を描く(枠だけ)

せっかくなので一捻り入れてみました.

package main

import (
    "os"
    "image"
    "image/jpeg"
    "image/color"
)

// 画像を単色に染める
func fillRect(img *image.RGBA, col color.Color) {
    // 矩形を取得
    rect := img.Rect

    // 全部埋める
    for h := rect.Min.Y; h < rect.Max.Y; h++ {
        for v := rect.Min.X; v < rect.Max.X; v++ {
            img.Set(v, h, col)
        }
    }
}

// 枠線を描く
func drawBounds(img *image.RGBA, col color.Color) {
    // 矩形を取得
    rect := img.Rect

    // 上下の枠
    for h := 0; h < rect.Max.X; h++ {
        // 上の枠
        img.Set(h, 0, col)
        // 下の枠
        img.Set(h, rect.Max.Y-1, col)
    }

    // 左右の枠
    for v := 0; v < rect.Max.Y; v++ {
        // 左の枠
        img.Set(0, v, col)
        // 右の枠
        img.Set(rect.Max.X-1, v, col)
    }
}

func main() {
    x := 0
    y := 0
    width := 100
    height := 50

    // RectからRGBAを作る(ゼロ値なので黒なはず)
    img := image.NewRGBA(image.Rect(x, y, width, height))
    // 白色に染める(透過なし)
    fillRect(img, color.RGBA{255, 255, 255, 0})
    // 赤枠を付ける
    drawBounds(img, color.RGBA{255, 0, 0, 0})

    // 出力用ファイル作成(エラー処理は略)
    file, _ := os.Create("sample.jpg")
    defer file.Close()

    // JPEGで出力(100%品質)
    if err := jpeg.Encode(file, img, &jpeg.Options{100}); err != nil {
        panic(err)
    }
}

[実行結果]

f:id:twinbird_htn:20170423001922j:plain

2.4. 円を描く
package main

import (
    "image"
    "image/color"
    "image/jpeg"
    "math"
    "os"
)

// 画像を単色に染める
func fillRect(img *image.RGBA, col color.Color) {
    // 矩形を取得
    rect := img.Rect

    // 全部埋める
    for h := rect.Min.Y; h < rect.Max.Y; h++ {
        for v := rect.Min.X; v < rect.Max.X; v++ {
            img.Set(v, h, col)
        }
    }
}

type Circle struct {
    p image.Point
    r int
}

// 円を描く
func (c *Circle) drawBounds(img *image.RGBA, col color.Color) {
    for rad := 0.0; rad < 2.0*float64(c.r); rad += 0.1 {
        x := int(float64(c.p.X) + float64(c.r)*math.Cos(rad))
        y := int(float64(c.p.Y) + float64(c.r)*math.Sin(rad))
        img.Set(x, y, col)
    }
}

func main() {
    x := 0
    y := 0
    width := 500
    height := 500

    // RectからRGBAを作る(ゼロ値なので黒なはず)
    img := image.NewRGBA(image.Rect(x, y, width, height))
    // 白色に染める(透過なし)
    fillRect(img, color.RGBA{255, 255, 255, 0})

    // 円を描く
    // 中心点
    center := image.Point{250, 250}
    // 円
    circle := Circle{center, 50}
    // 描く
    circle.drawBounds(img, color.RGBA{255, 0, 0, 0})

    // 出力用ファイル作成(エラー処理は略)
    file, _ := os.Create("sample.jpg")
    defer file.Close()

    // JPEGで出力(100%品質)
    if err := jpeg.Encode(file, img, &jpeg.Options{100}); err != nil {
        panic(err)
    }
}

[実行結果]

f:id:twinbird_htn:20170423001935j:plain

3. ファイルから画像を読み込み加工する

グラフィックも面白いですが, 画像加工がしてみたいので色々やってみましょう.

元画像はやはりレナさんで.

f:id:twinbird_htn:20170423002030p:plain

3.1. 画像を読み込み, サイズを調べる
package main

import (
    "image"
    _ "image/png"
    "os"
    "fmt"
)

func main() {
    // 画像ファイルを開く
    file, _ := os.Open("./rena.png")
    defer file.Close()

    // 設定をデコード
    config, formatName, err := image.DecodeConfig(file)
    if err != nil {
        panic(err)
    }
    // フォーマット名表示
    fmt.Println(formatName)
    // サイズ表示
    fmt.Println(config.Width)
    fmt.Println(config.Height)
}

[実行結果]

$ ./image.exe
png
512
512
3.2. 画像を合成する

https://golang.org/doc/gopher/に自由に使えるgopher君の画像があるので合成してみます.

package main

import (
    "image"
    "image/png"
    "os"
    "image/draw"
)

func main() {
    // 画像ファイルを開く(書き込み元)
    src, _ := os.Open("./run.png")
    defer src.Close()
    // 画像ファイルを開く(書き込み先)
    dst, _ := os.Open("./rena.png")
    defer dst.Close()

    // デコードしてイメージオブジェクトを準備
    srcImg, _, err := image.Decode(src)
    if err != nil {
        panic(err)
    }
    dstImg, _, err := image.Decode(dst)
    if err != nil {
        panic(err)
    }

    // 書き出し用のイメージを準備
    outRect := image.Rectangle{image.Pt(0, 0), dstImg.Bounds().Size()}
    out := image.NewRGBA(outRect)

    // 描画する
    // 元画像をまず描く
    dstRect := image.Rectangle{image.Pt(0, 0), dstImg.Bounds().Size()}
    draw.Draw(out, dstRect, dstImg, image.Pt(0, 0), draw.Src)
    // 上書きする
    srcRect := image.Rectangle{image.Pt(0, 0), srcImg.Bounds().Size()}
    draw.Draw(out, srcRect, srcImg, image.Pt(0, 0), draw.Over)

    // 書き出し用ファイル準備
    outfile, _ := os.Create("out.png")
    defer outfile.Close()
    // 書き出し
    png.Encode(outfile, out)
}

[実行結果]

f:id:twinbird_htn:20170423002122p:plain

なんかえらくシュールな画像になってしまった…

4. もっと画像処理する

せっかくなのでもう少し色々やります.

4.1. グレースケール化する

標準パッケージに用意されています.

package main

import (
    "image"
    "image/png"
    "image/color"
    "os"
)

func main() {
    // 画像ファイルを開く(書き込み元)
    src, _ := os.Open("./rena.png")
    defer src.Close()

    // デコードしてイメージオブジェクトを準備
    srcImg, _, err := image.Decode(src)
    if err != nil {
        panic(err)
    }
    srcBounds := srcImg.Bounds()

    // 出力用イメージ
    dest := image.NewGray(srcBounds)

    // グレー化
    for v := srcBounds.Min.Y; v < srcBounds.Max.Y; v++ {
        for h := srcBounds.Min.X; h < srcBounds.Max.X; h++ {
            c := color.GrayModel.Convert(srcImg.At(h, v))
            gray, _ := c.(color.Gray)
            dest.Set(h, v, gray)
        }
    }

    // 書き出し用ファイル準備
    outfile, _ := os.Create("out.png")
    defer outfile.Close()
    // 書き出し
    png.Encode(outfile, dest)
}

[実行結果]

f:id:twinbird_htn:20170423002146p:plain

4.2. 2値化する

ちょっと強引かな.

ColorModelとConverterを自作するのが正統派なんですかね.

いずれにしても関数化ぐらいしてもよかったかも.

package main

import (
    "image"
    "image/png"
    "image/color"
    "os"
)

// 二値化のしきい値
const threshold = 128

func main() {
    // 画像ファイルを開く(書き込み元)
    src, _ := os.Open("./rena.png")
    defer src.Close()

    // デコードしてイメージオブジェクトを準備
    srcImg, _, err := image.Decode(src)
    if err != nil {
        panic(err)
    }
    srcBounds := srcImg.Bounds()

    // 出力用イメージ
    dest := image.NewGray(srcBounds)

    // 二値化
    for v := srcBounds.Min.Y; v < srcBounds.Max.Y; v++ {
        for h := srcBounds.Min.X; h < srcBounds.Max.X; h++ {
            c := color.GrayModel.Convert(srcImg.At(h, v))
            gray, _ := c.(color.Gray)
            // しきい値で二値化
            if gray.Y > threshold {
                gray.Y = 255
            } else {
                gray.Y = 0
            }
            dest.Set(h, v, gray)
        }
    }

    // 書き出し用ファイル準備
    outfile, _ := os.Create("out.png")
    defer outfile.Close()
    // 書き出し
    png.Encode(outfile, dest)
}

[実行結果]

f:id:twinbird_htn:20170423002210p:plain

まとめ

もうちょっと色々遊ぼうかと思ってましたが, この辺にしときます.

ベースは標準ライブラリで提供されているので, 画像処理アルゴリズムを試したりするのにはちょうどいい気がします.

本格的にやる際には自作でライブラリを用意するほうがいいかもしれないです.
(そしてそれが面白かったりしますよね)

参考

package - Image

package - Image/draw

package - Image/png

package - Image/jpeg

package - Image/color

The Go Blog - The Go image/draw package

とある子育てパパの日記 - 2つの画像を重ねる/golang

msys2(msys2-launcher利用下)で環境変数を引き継ぎたい

msys2を使っているとwindows標準の環境変数が引き継がれなくてしょんぼりする.

探していると対処法があるそうでめもらんだむ - MSYS2 で PATH が引き継がれない

この記事はホントにありがたかったんですが, msys2-launchar使ってるせいか, うまくいきませんでした.

あれこれ見てみましたが, 最終的にはmsys2.iniの以下を変更するとうまくいきました.

#MSYS=winsymlinks:nativestrict
#MSYS=error_start:mingw64/bin/qtcreator.exe|-debug|<process-id>
#CHERE_INVOKING=1
#MSYS2_PATH_TYPE=inherit  # <= ここの#を外す
MSYSTEM=MSYS 

これで今度から少し楽です.

Docker Toolboxを使ってWindows10 HomeでDocker環境を構築する

DockerをWindowsで使おうとするとHyper-VのあるProfessional以上の環境を要求されます.

まぁ, 買えばいいんですが, そうはいってもない状況で使いたい場合もあるんですね.

そういう方はVirtualbox上で構築できるDocker Toolboxというものがあるそうです.

そりゃいいねと. そういうわけで使ってみました.

0. 環境

64bit環境だとIntel VTのCPUがいるらしいです.

私が試した環境は

です

1. ダウンロード

まずはダウンロードします.

URLは以下です.

Docker Toolbox

2. インストールする

基本はひたすら「Next」です.

VirtualBoxやgit for windowsが入っている場合はチェックは不要です.

Install VirtualBox with NDIS5 driver[default NDIS6]にチェックを入れないとネットワークがうまくいかない例があるっぽいので入れておきます

こちらの記事で手順の中でNDIS5を指定しているので, そういうことかなと.

3. 起動する

デスクトップにショートカット(Docker Quickstart Terminal)が出来ているので実行します.

起動すると勝手にVMを作成したりDocker-imageをダウンロードしたりしてくれます.

応援しながら待ちます.

4.runしてみる

以下が動けば大丈夫でしょう.

docker run hello-world

Virtual boxの画面を開くとdefaultマシンが一つできています.

あー

Qiitaに同種の記事がありました.

たぶんこっち見た方がいいですね…詳しいし

golangでcontextパッケージを使う

使わないから全然覚えられない.

とりあえずサンプルを書いて覚えておく.

詳細や思想はdeeeetさんの記事が非常にわかりやすいのでこれ読めばそれでいいと思う.

大雑把な理解

  1. context.Backgroundでcontextを作るか, よそからもらってきたcontextを使って
  2. With****メソッドを使ってcontextに意味を持たせる
  3. 結果や値はcontextオブジェクトの中に入ってる
メソッド

With****メソッドは以下がある.

メソッド名 用途
WithCancel キャンセル可能にする
WithTimeout 一定時間でキャンセルされるようにする
WithDeadline 指定時間でキャンセルされるようにする
WithValue 値を一緒に引き渡す

どれも新しいcontextオブジェクトを返してくれる.

メンバ

contextのインターフェースは以下のメンバでとてもシンプル. コメントで書いてる意味がホントに正しいかはちょっと怪しいけど.

type Context interface {
    // デッドライン時刻とデッドラインが設定されているかどうか(falseなら未設定)
    Deadline() (deadline time.Time, ok bool)
    // 完了/キャンセルを知らせるチャネル
    Done() <-chan struct{}
    // エラー
    Err() error
    // WithValueで持ち運ぶ値
    Value(key interface{}) interface{}
}

キャンセル可能にする

キャンセル可能にするにはWithCancelを使います.

以下は無限ループで助けを呼び続けるゴルーチンを2秒後にキャンセルする例です.

WithCancelで返される2つ目の関数を呼び出せばキャンセルされるんですね.

package main

import (
    "context"
    "fmt"
    "time"
)

func infiniteLoop(ctx context.Context) {
    // 終わらないやつ
    for {
        fmt.Println("Help!")
    }
}

func main() {
    ctx := context.Background()
    ctx, cancel := context.WithCancel(ctx)

    go infiniteLoop(ctx)

    // 2秒待ってキャンセルする
    time.Sleep(2 * time.Second)
    cancel()

    select {
    case <- ctx.Done():
        fmt.Println(ctx.Err())
    }
}

[実行結果]

Help!
Help!
Help!
.
.
.
Help!
Help!
context canceled
Help!
Help!
Help!
Help!

プロセスが死ぬまでゴルーチンが生きてるので, ずっと助けてと叫んでますが….

指定時間経過したらキャンセルする

上記の例だとWithTimeoutを使ってもっとシンプルに書くことができます.

package main

import (
    "context"
    "fmt"
    "time"
)

func infiniteLoop(ctx context.Context) {
    // 終わらないやつ
    for {
        fmt.Println("Help!")
    }
}

func main() {
    ctx := context.Background()

    // 2秒待ってキャンセルする
    ctx, cancel := context.WithTimeout(ctx, 2 * time.Second)
    defer cancel()

    go infiniteLoop(ctx)

    select {
    case <- ctx.Done():
        fmt.Println(ctx.Err())
    }
}

[実行結果]

Help!
Help!
Help!
Help!
.
.
.
Help!
Help!
Help!
context deadline exceeded
Help!
Help!
Help!
Help!
Help!
Help!
Help!

指定時間になったらキャンセルする

相対的な時刻指定だけではなく, 絶対的な時刻指定も行う事ができます.

package main

import (
    "context"
    "fmt"
    "time"
)

func infiniteLoop(ctx context.Context) {
    // 終わらないやつ
    for {
        fmt.Println("Help!")
    }
}

func main() {
    ctx := context.Background()

    // 2秒後をデッドラインにする
    ctx, cancel := context.WithDeadline(ctx, time.Now().Add(2 * time.Second))
    defer cancel()

    go infiniteLoop(ctx)

    select {
    case <- ctx.Done():
        fmt.Println(ctx.Err())
    }
}

[実行結果]

Help!
Help!
Help!
Help!
.
.
.
Help!
Help!
Help!
Help!
context deadline exceeded
Help!

時間設定したけどやっぱり手動でキャンセルする

WithTimeoutやWithDeadlineでキャンセル時刻を指定していても, 戻り値のcancel関数を呼び出せば好きなタイミングでキャンセルを行えます.

package main

import (
    "context"
    "fmt"
    "time"
)

func infiniteLoop(ctx context.Context) {
    // 終わらないやつ
    for {
        fmt.Println("Help!")
    }
}

func main() {
    ctx := context.Background()

    // 2秒後をデッドラインにする
    ctx, cancel := context.WithDeadline(ctx, time.Now().Add(2 * time.Second))
    defer cancel()

    // やっぱすぐやめた
    cancel()

    go infiniteLoop(ctx)

    select {
    case <- ctx.Done():
        fmt.Println(ctx.Err())
    }
}

[実行結果]

$ ./context.exe
Help!
context canceled
Help!
Help!
Help!
Help!

キャンセルを伝搬させる

流石にプロセス死ぬまで叫び続けるのはかわいそうなので, キャンセル時には脱出させてあげます.

contextを作成する際にcontextを渡してあげると, Done()を経由してcancelが伝搬していきます.

package main

import (
    "context"
    "fmt"
)

func infiniteLoop(ctx context.Context) {
    innerCtx, cancel := context.WithCancel(ctx)
    defer cancel()
    // 終わらないやつ だったのを終わるようにした
    for {
        fmt.Println("Help!")

        select {
        case <- innerCtx.Done():
            fmt.Println("Exit from hell.")
            return
        }
    }
}

func main() {
    ctx := context.Background()
    ctx, cancel := context.WithCancel(ctx)

    // 無限ループに入る
    go infiniteLoop(ctx)

    // やっぱすぐやめた
    cancel()

    select {
    case <- ctx.Done():
        fmt.Println(ctx.Err())
    }
}

[実行結果]

$ ./context.exe
Help!
Exit from hell.
context canceled

地獄から出てこれました.よかったですね.

値を引き渡す

contextを使って値を渡していく時にはcontext.WithValueを使います.

値を取得するにはcontext.Valueにキーを渡して取得します.
(interface型が返るので, 型アサーションがいりますね)

package main

import (
    "context"
    "fmt"
)

func main() {
    ctx := context.Background()
    ctx = context.WithValue(ctx, "hoge", 1)

    fmt.Println(ctx.Value("hoge").(int))
}

[実行結果]

$ ./context.exe
1
追記

deeeetさんのeが一つ多かったので訂正しました.(ごめんなさい)

@niconegotoさんありがとうございました