2013-09-06 34 views
31

Tôi chắc chắn rằng có một lời giải thích đơn giản cho tình huống tầm thường này, nhưng tôi mới với mô hình đồng thời go.Tại sao sử dụng kênh không bị chặn trong cùng một goroutine cho một bế tắc

khi tôi chạy ví dụ này

package main 

import "fmt" 

func main() { 
    c := make(chan int)  
    c <- 1 
    fmt.Println(<-c) 
} 

tôi nhận được lỗi này:

fatal error: all goroutines are asleep - deadlock! 

goroutine 1 [chan send]: 
main.main() 
    /home/tarrsalah/src/go/src/github.com/tarrsalah/tour.golang.org/65.go:8 +0x52 
exit status 2 

Tại sao?


Bao bì c <- trong một goroutine làm cho ví dụ chạy như chúng tôi mong đợi

package main 

import "fmt" 

func main() { 
    c := make(chan int)   
    go func(){ 
     c <- 1 
    }() 
    fmt.Println(<-c) 
} 

Một lần nữa, tại sao?

Xin vui lòng, tôi cần giải thích sâu sắc, không chỉ làm thế nào để loại bỏ bế tắc và sửa mã.

Trả lời

49

Từ the documentation:

Nếu kênh là không có bộ đệm, các khối người gửi đến người nhận đã nhận được giá trị. Nếu kênh có bộ đệm, người gửi chỉ chặn cho đến khi giá trị đã được sao chép vào bộ đệm; nếu bộ đệm đầy, điều này có nghĩa là chờ cho đến khi một số người nhận đã truy xuất một giá trị.

Said khác:

  • khi một kênh là đầy đủ, chờ đợi người gửi cho goroutine khác để thực hiện một số phòng bằng cách nhận
  • bạn có thể thấy một kênh không có bộ đệm như một luôn luôn đầy đủ một: có phải là một goroutine khác để nhận những gì người gửi gửi.

Dòng này

c <- 1 

khối vì kênh là không có bộ đệm. Vì không có goroutine nào khác nhận giá trị, tình huống không thể giải quyết được, đây là bế tắc.

Bạn có thể làm cho nó không chặn bằng cách thay đổi tạo kênh để

c := make(chan int, 1) 

để có chỗ cho một mục trong kênh trước khi nó ngăn chặn.

Nhưng đó không phải là điều tương tranh. Thông thường, bạn sẽ không sử dụng một kênh mà không có goroutines khác để xử lý những gì bạn đặt bên trong.Bạn có thể định nghĩa một goroutine nhận như thế này:

func main() { 
    c := make(chan int)  
    go func() { 
     fmt.Println("received:", <-c) 
    }() 
    c <- 1 
} 

Demonstration

+3

Tôi thấy khó mà nghĩ, có ba tình huống: 1) kênh không bị chặn mà không có goroutine mới -> bế tắc, 2) kênh đệm mà không có goroutine mới -> không bế tắc 3) kênh không bị chặn với goroutine mới -> chạy. – tarrsalah

+14

Khi kênh đầy, phải có một goroutine để nhận những gì người gửi gửi đi, hoặc người gửi khác chặn cho đến khi kênh đến. Bạn có thể thấy một kênh không bị chặn là kênh luôn đầy đủ. –

+0

Cảm ơn @ dystry, tôi sẽ suy nghĩ về nó, chạy một số mã, đọc về đi đồng thời nhiều hơn ... – tarrsalah

5

Trong kênh unbuffered bằng văn bản cho kênh sẽ không xảy ra cho đến khi phải có một số người nhận được chờ đợi để nhận được dữ liệu, có nghĩa là trong ví dụ dưới đây

func main(){ 
    ch := make(chan int) 
    ch <- 10 /* Main routine is Blocked, because there is no routine to receive the value */ 
    <- ch 
} 

Now Trong trường hợp chúng tôi có thói quen đi khác, cùng một nguyên tắc áp dụng

func main(){ 
    ch :=make(chan int) 
    go task(ch) 
    ch <-10 
} 
func task(ch chan int){ 
    <- ch 
} 

Thao tác này sẽ hoạt động vì tác vụ thường lệ đang chờ dữ liệu được tiêu thụ trước khi ghi xảy ra với kênh không bị chặn.

Để làm rõ hơn, hãy trao đổi thứ tự các câu lệnh thứ hai và thứ ba trong chức năng chính.

func main(){ 
    ch := make(chan int) 
    ch <- 10  /*Blocked: No routine is waiting for the data to be consumed from the channel */ 
    go task(ch) 
} 

Điều này sẽ dẫn đến bế tắc

Vì vậy, trong ngắn hạn, ghi vào kênh unbuffered chỉ xảy ra khi có một số thói quen chờ đợi để đọc từ kênh, khác các hoạt động viết bị chặn vĩnh viễn và dẫn đến bế tắc.

CHÚ Ý: Khái niệm tương tự áp dụng cho kênh đệm, nhưng người gửi không bị chặn cho đến khi bộ đệm đầy, nghĩa là người nhận không nhất thiết phải đồng bộ với mọi thao tác ghi.

Vì vậy, nếu chúng tôi đã đệm kênh có kích thước 1, sau đó mã nêu trên bạn sẽ làm việc

func main(){ 
    ch := make(chan int, 1) /*channel of size 1 */ 
    ch <-10 /* Not blocked: can put the value in channel buffer */ 
    <- ch 
} 

Nhưng nếu chúng ta viết thêm giá trị cho ví dụ trên, sau đó bế tắc sẽ xảy ra

func main(){ 
    ch := make(chan int, 1) /*channel Buffer size 1 */ 
    ch <- 10 
    ch <- 20 /*Blocked: Because Buffer size is already full and no one is waiting to recieve the Data from channel */ 
    <- ch 
    <- ch 
} 
0

Trong câu trả lời này, tôi sẽ cố gắng giải thích thông báo lỗi mà qua đó chúng tôi có thể xem xét một chút về cách hoạt động của kênh và goroutines

Ví dụ đầu tiên là:

package main 

import "fmt" 

func main() { 
    c := make(chan int)  
    c <- 1 
    fmt.Println(<-c) 
} 

Các thông báo lỗi là:

fatal error: all goroutines are asleep - deadlock! 

Trong đoạn mã, có NO goroutines ở tất cả (BTW lỗi này là trong thời gian chạy, không thời gian biên dịch). Khi chạy dòng này c <- 1, nó muốn đảm bảo rằng thông báo trong kênh sẽ được nhận ở đâu đó (ví dụ: <-c). Đi không biết nếu kênh sẽ được nhận hay không vào thời điểm này. Vì vậy, đi sẽ chờ cho goroutines chạy để kết thúc cho đến khi một trong hai điều sau đây sẽ xảy ra:

  1. tất cả các goroutines là xong (ngủ)
  2. một trong những goroutine cố gắng để nhận được các kênh

Trong trường hợp # 1, đi sẽ lỗi với thông báo trên, vì bây giờ đi BIẾT rằng không có cách nào mà một goroutine sẽ nhận được các kênh và nó cần một.

Trong trường hợp # 2, chương trình sẽ tiếp tục, vì bây giờ hãy BIẾT rằng kênh này được nhận. Điều này giải thích trường hợp thành công trong ví dụ của OP.

Các vấn đề liên quan