2012-04-14 26 views
7

Là một bài tập luyện cơ bản ngớ ngẩn, tôi đã cố gắng triển khai sleeping barber problem trong golang. Với các kênh này, điều này khá dễ dàng, nhưng tôi đã gặp phải một heisenbug. Đó là, khi tôi cố gắng chẩn đoán nó, vấn đề biến mất!In ấn để stdout khiến goroutine bị chặn chạy?

Hãy xem xét những điều sau đây. Chức năng main() đẩy các số nguyên (hoặc "khách hàng") vào kênh shop. barber() đọc kênh shop để cắt tóc "của khách hàng". Nếu tôi chèn câu lệnh fmt.Print vào hàm customer(), chương trình sẽ chạy như mong đợi. Nếu không, barber() không bao giờ cắt tóc của bất kỳ ai.

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) 
    } 
} 

Bất kỳ ý tưởng nào đang diễn ra?

Trả lời

5

Vấn đề là cách trình lập lịch biểu của Go được triển khai. Goroutine hiện tại chỉ có thể sinh ra các goroutine khác khi nó thực hiện một cuộc gọi hệ thống hoặc một hoạt động kênh chặn. fmt.Println thực hiện cuộc gọi hệ thống, tạo cơ hội cho goroutine sinh lợi. Nếu không, nó không có.

Trong thực tế, điều này thường không quan trọng, nhưng đối với những vấn đề nhỏ như thế này đôi khi nó có thể tăng lên.

Ngoài ra, một cách ít đặc sắc thành ngữ hơn để làm một non-blocking gửi trên một kênh là: bên ngoài

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

Cách bạn đang làm việc đó, khách hàng có thể kết thúc chờ đợi của thợ cắt tóc cửa hàng kể từ khi bạn thực sự gửi, len(shop) có thể đã thay đổi.

+1

Câu trả lời của bạn được giữ nếu chỉ có một luồng được thời gian chạy được sử dụng. Thiết lập GOMAXPROCS hoặc thông qua các cuộc gọi thời gian chạy như lazy1 nói hoặc thiết lập biến môi trường cũng sẽ cho phép bất kỳ goroutine nào chạy song song với các goroutine khác trên một luồng riêng biệt. Nó có thể là giá trị mở rộng câu trả lời của bạn để phản ánh cách thời gian chạy của multiplexes goroutines trên các chủ đề có sẵn. –

1

Không thêm runtime.GOMAXPROCS(2) vào đầu giải quyết chính này?

+0

Thực tế là như vậy. Ngay cả trên máy đơn lõi của tôi ... – Jjed

+0

Thậm chí nếu nó sửa chữa nó, nó không phải là cách tiếp cận đúng. –

+0

@MattJoiner Bạn có thể xây dựng? – lazy1