LoginSignup
2
1

More than 3 years have passed since last update.

goroutineで大食い大会

Last updated at Posted at 2019-08-27

はじめに

ゴルーチンとチャネルの勉強として大食い大会を書いてみた。

1)コックはひたすらホットドッグを作る
2)ウェイターはひたすらホットドッグをフードファイターへ運ぶ
3)フードファイターはひたすらホットドッグを食べて、自分が食べたホットドッグをカウントする
4)制限時間になったら大会終了。その時点でのランキングを出す。

この 1 => 2 => 3 の流れをチャネルを使い、4の制限時間の制御にcontextを使った。

コード


package main

import (
    "context"
    "fmt"
    "time"

    . "github.com/ahmetb/go-linq"
)

func cook(
    done <-chan interface{},
    fn func() interface{},
) <-chan interface{} {
    foods := make(chan interface{})
    go func() {
        defer close(foods)
        for {
            select {
            case <-done:
                return
            case foods <- fn():
            }
        }
    }()
    return foods
}

func hotDog() interface{} {
    fmt.Printf("コックはホットドッグを作ります\n")

    return "ホットドッグ"
}

func waiter(foods <-chan interface{}) interface{} {
    food := <-foods
    fmt.Printf("ウェイターは%vをフードファイターへ持っていきます。\n", food)
    return food
}

func waitperson(
    done <-chan interface{},
    foods <-chan interface{},
) <-chan interface{} {
    toFoodFighter := make(chan interface{})
    go func() {
        defer close(toFoodFighter)
        for {
            select {
            case <-done:
                return
            case toFoodFighter <- waiter(foods):
            }
        }
    }()
    return toFoodFighter
}

type FoodFighter struct {
    name    string
    current int
}

type FoodFighters []*FoodFighter

func (f *FoodFighter) eat(foods <-chan interface{}) *FoodFighter {
    food := <-foods
    f.current++
    fmt.Printf("%vは%vを%v個食べた\n", f.name, food, f.current)
    return f
}

func (f *FoodFighter) startEat(
    done <-chan interface{},
    foods <-chan interface{},
) <-chan *FoodFighter {
    results := make(chan *FoodFighter)
    go func() {
        defer close(results)
        for {
            select {
            case <-done:
                return
            case results <- f.eat(foods):
            }
        }
    }()
    return results
}

func start(done <-chan interface{}, f *FoodFighter) <-chan *FoodFighter {
    return f.startEat(done, waitperson(done, cook(done, hotDog)))
}

func competition(ctx context.Context, cancel context.CancelFunc, done <-chan interface{}) FoodFighters {
    defer cancel()
    var foodFighters FoodFighters
    var f1Result *FoodFighter
    var f2Result *FoodFighter
    var f3Result *FoodFighter
    f1 := &FoodFighter{name: "ボブ"}
    f2 := &FoodFighter{name: "ビル"}
    f3 := &FoodFighter{name: "マイケル"}

    for {
        select {
        case f1Result = <-start(done, f1):
        case f2Result = <-start(done, f2):
        case f3Result = <-start(done, f3):
        case <-ctx.Done():
            fmt.Println("大会終了です\n")
            foodFighters = append(foodFighters, f1Result)
            foodFighters = append(foodFighters, f2Result)
            foodFighters = append(foodFighters, f3Result)

            return foodFighters
        }
    }
}

func displayResult(results FoodFighters) {
    time.Sleep(1 * time.Second)
    fmt.Println("大会結果発表\n")

    sortedResults := From(results).Sort(func(f1, f2 interface{}) bool {
        return f1.(*FoodFighter).current > f2.(*FoodFighter).current
    }).Results()

    for i, fighter := range sortedResults {
        fmt.Printf("%v位: %v %v個\n", i+1, fighter.(*FoodFighter).name, fighter.(*FoodFighter).current)
    }
}

func foodFight() {
    done := make(chan interface{})
    defer close(done)

    limitTime := 2 * time.Second
    ctx, cancel := context.WithTimeout(context.Background(), limitTime)
    results := competition(ctx, cancel, done)

    displayResult(results)
}

func main() {
    foodFight()
}

実行結果

:
:
マイケルはホットドッグを417個食べた
ウェイターはホットドッグをフードファイターへ持っていきます。
ウェイターはホットドッグをフードファイターへ持っていきます。
コックはホットドッグを作ります
コックはホットドッグを作ります
ボブはホットドッグを447個食べた
コックはホットドッグを作ります
ビルはホットドッグを442個食べた
ウェイターはホットドッグをフードファイターへ持っていきます。
大会結果発表

1位: ボブ 447個
2位: ビル 442個
3位: マイケル 418個

goではselectのcase文全体に対して 擬似乱数による一様選択1 をしているらしい。
それぞれのcaseは等しく選択されていく。なので、結果は毎回、拮抗した感じになる。

2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1