いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-169 (HelloWorldで遊ぶ)(同期、非同期)

概要

最初にプログラム作ったときのサンプルって大抵HelloWorldですよね。

その言語のプログラムの基本構造、出力の方法を学べる基本のプログラムだと思いますが、たまにこれで遊ぶと面白いし勉強にもなります。

てことで、以下サンプルです。

同期版

// Package helloworld -- Go言語での Hello World プログラムが配置されているパッケージです。
package helloworld

import "fmt"

// Sync -- GO言語でのHelloWorldサンプル (同期版)
func Sync() error {
    // Golang には、 ビルドイン関数の println と
    // fmt.Println という 名前の似ている2つの関数があるが
    // 基本的に、どのサンプルも fmt.Println を利用している。
    //
    // 理由は、Golang のドキュメントに以下のように記載されているから。
    //   https://golang.org/builtin/#println
    //   https://qiita.com/taji-taji/items/79a49c0ee329d0b9c065
    for i := 0; i < 10; i++ {
        fmt.Printf("[%02d]\tHello World\n", i+1)
    }

    return nil
}

実行すると以下のようになります。

gitpod /workspace/try-golang $ make run
go run github.com/devlights/try-golang/cmd/trygolang -onetime -example ""

ENTER EXAMPLE NAME: helloworld_sync

[Name] "helloworld_sync"
[01]    Hello World
[02]    Hello World
[03]    Hello World
[04]    Hello World
[05]    Hello World
[06]    Hello World
[07]    Hello World
[08]    Hello World
[09]    Hello World
[10]    Hello World


[Elapsed] 19.398µs

非同期版

package helloworld

import (
    "context"
    "fmt"
    "math/rand"
    "time"

    "github.com/devlights/gomy/ctxs"
)

// Async -- HelloWorld 非同期版
func Async() error {
    // main contexts
    var (
        rootCtx          = context.Background()
        mainCtx, mainCxl = context.WithCancel(rootCtx)
    )
    defer mainCxl()

    // proc context
    var (
        timeLimit        = 100 * time.Millisecond
        procCtx, procCxl = context.WithTimeout(mainCtx, timeLimit)
    )
    defer procCxl()

    // start tasks
    var tasks []context.Context
    for i := 0; i < 10; i++ {
        tasks = append(tasks, func(pCtx context.Context, no, delay int) context.Context {
            var (
                ctx, cxl = context.WithCancel(pCtx)
            )

            go func() {
                defer cxl()

                select {
                case <-ctx.Done():
                    fmt.Printf("[%02d]\tTime out\t(%02d msec delay)\n", no, delay)
                case <-time.After(time.Duration(delay) * time.Millisecond):
                    fmt.Printf("[%02d]\tHello World\t(%02d msec delay)\n", no, delay)
                }
            }()

            return ctx
        }(procCtx, i+1, rand.Intn(100)))
    }

    // wait until all tasks are completed
    <-ctxs.WhenAll(procCtx, tasks...).Done()

    return nil
}

実行すると以下のようになります。

gitpod /workspace/try-golang $ make run
go run github.com/devlights/try-golang/cmd/trygolang -onetime -example ""

ENTER EXAMPLE NAME: helloworld_async

[Name] "helloworld_async"
[03]    Hello World     (02 msec delay)
[08]    Hello World     (02 msec delay)
[04]    Hello World     (13 msec delay)
[09]    Hello World     (48 msec delay)
[06]    Hello World     (49 msec delay)
[02]    Hello World     (59 msec delay)
[10]    Hello World     (74 msec delay)
[07]    Hello World     (75 msec delay)
[05]    Hello World     (82 msec delay)
[01]    Hello World     (83 msec delay)


[Elapsed] 84.097568ms

同期と非同期を混ぜ混ぜ

package helloworld

import (
    "context"
    "time"

    "github.com/devlights/gomy/ctxs"
    "github.com/devlights/gomy/output"
)

// Mixed -- 同期と非同期の両方で同じことをするサンプル
func Mixed() error {
    // main contexts
    var (
        rootCtx          = context.Background()
        mainCtx, mainCxl = context.WithCancel(rootCtx)
    )
    defer mainCxl()

    // proc context
    var (
        procCtx, procCxl = context.WithTimeout(mainCtx, 1*time.Second)
    )
    defer procCxl()

    // start tasks
    var (
        syncCtx  = sync(procCtx)
        asyncCtx = async(procCtx)
    )

    // wait until all tasks are completed
    <-ctxs.WhenAll(procCtx, syncCtx, asyncCtx).Done()

    return nil
}

func sync(pCtx context.Context) context.Context {
    var (
        ctx, cxl = context.WithCancel(pCtx)
    )

    go func() {
        defer cxl()

        for v := range items() {
            v := v
            <-exec(ctx, v+1, "sync ").Done()
        }
    }()

    return ctx
}

func async(pCtx context.Context) context.Context {
    var (
        ctx, cxl = context.WithCancel(pCtx)
        tasks    = make([]context.Context, 0)
    )

    for v := range items() {
        v := v
        tasks = append(tasks, exec(ctx, v+1, "async"))
    }

    go func() {
        defer cxl()
        <-ctxs.WhenAll(ctx, tasks...).Done()
    }()

    return ctx
}

func items() <-chan int {
    var (
        ch = make(chan int)
    )

    go func() {
        defer close(ch)

        for i := 0; i < 10; i++ {
            ch <- i
        }
    }()

    return ch
}

func exec(pCtx context.Context, v int, prefix string) context.Context {
    var (
        ctx, cxl = context.WithCancel(pCtx)
    )

    go func() {
        defer cxl()

        select {
        case <-ctx.Done():
            return
        default:
            output.Stderrf(prefix, "[%02d] helloworld\n", v)
        }
    }()

    return ctx
}

実行すると以下のようになります。

gitpod /workspace/try-golang $ make run
go run github.com/devlights/try-golang/cmd/trygolang -onetime -example ""

ENTER EXAMPLE NAME: helloworld_mixed

[Name] "helloworld_mixed"
async                [02] helloworld
sync                 [01] helloworld
sync                 [02] helloworld
async                [01] helloworld
sync                 [03] helloworld
sync                 [04] helloworld
sync                 [05] helloworld
sync                 [06] helloworld
sync                 [07] helloworld
sync                 [08] helloworld
async                [04] helloworld
async                [06] helloworld
async                [05] helloworld
sync                 [09] helloworld
async                [08] helloworld
sync                 [10] helloworld
async                [10] helloworld
async                [07] helloworld
async                [09] helloworld
async                [03] helloworld


[Elapsed] 548.179µs

syncの表示は逐次的になっていて(1から10まで順に出力)、asyncの表示は非同期となっていますね。


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com