2012-04-14 7 views
7

私は愚かな基本的なスレッド演習として、sleeping barber problemをgolangに実装しようとしています。チャンネルでこれは非常に簡単にする必要がありますが、私はheisenbugを実行しました。つまり、私がそれを診断しようとすると、問題は消えてしまいます!stdoutに印刷するとブロックされたgoroutineが実行されますか?

以下を考慮してください。 main()関数は、整数(または「顧客」)をshopチャネルにプッシュします。 barber()shopチャンネルを読み込み、「顧客」の髪をカットします。 fmt.Printステートメントをcustomer()関数に挿入すると、プログラムが正常に実行されます。そうでなければ、barber()は決して誰の髪をも切らない。

package main 

import "fmt" 

func customer(id int, shop chan<- int) { 
    // Enter shop if seats available, otherwise leave 
    // fmt.Println("Uncomment this line and the program works") 
    if len(shop) < cap(shop) { 
     shop <- id 
    } 
} 

func barber(shop <-chan int) { 
    // Cut hair of anyone who enters the shop 
    for { 
     fmt.Println("Barber cuts hair of customer", <-shop) 
    } 
} 

func main() { 
    shop := make(chan int, 5) // five seats available 
    go barber(shop) 
    for i := 0; ; i++ { 
     customer(i, shop) 
    } 
} 

何が起こっているのでしょうか?

答えて

5

問題は、Goのスケジューラの実装方法です。現在のゴルーチンは、システムコールまたはブロッキングチャネル操作を行うときにのみ、他のゴルーチンに渡すことができます。 fmt.Printlnはシステムコールを行い、ゴルーチンに収穫の機会を与えます。そうでなければ、それは持っていません。

実際にはこれは重要ではありませんが、このような小さな問題については、時には切り詰めることがあります。また

、チャネル上で送信非ブロックを行うための、より慣用的な、より少ない際どい方法がある:あなたがそれをやっている

func customer(id int, shop chan<- int) { 
    // Enter shop if seats available, otherwise leave 
    select { 
    case shop <- id: 
    default: 
    } 
} 

方法、顧客は床屋の外で待って終わる可能性あなたが実際に送付した時点で、len(shop)が変更されている可能性があります。

+1

goランタイムでスレッドが1つしか使用されていない場合、あなたの答えが保持されます。 lazy1のように実行時呼び出しでGOMAXPROCSを設定するか、環境変数を設定することで、任意のゴルーチンを別のゴルーチンと並列に実行することができます。使用可能なスレッドでgoroutineをランタイムに多重化する方法を反映するために、答えを広げる価値があるかもしれません。 –

1

メインの冒頭にruntime.GOMAXPROCS(2)を追加するとこれが解決しますか?

+0

実際にはそうです。私のシングルコアマシンでさえ... – Jjed

+0

それを修正しても正しいアプローチではありません。 –

+0

@MattJoinerあなたは精巧にできますか? – lazy1

関連する問題