2016-02-24 22 views
5

Jestem ciekawy, dlaczego poniższe nie działa. W ogóle select z default: zapobiega impasu, ale nie w tym przypadku:wybierz kanał <- <- kanał

package main 

import "fmt" 

func main() { 
    a := make(chan int) 
    b := make(chan int) 

    select { 
    case a <- <- b: 
     fmt.Println("this is impossible") 
    default: 
     fmt.Println("select worked as naively expected") 
    } 
} 

Oczywiście nie lubi <- <- ale zastanawiam się, co dzieje się za powierzchnią tutaj. W innych sytuacjach dozwolona jest opcja <- <- (choć może nie zalecana).

Odpowiedz

6

a <- <- b jest taka sama jak a<- (<-b), ponieważ operator <- stowarzyszone z lewej skrajnej chan możliwe.

Tak więc select ma case z operacją wysyłania (w postaci a<- (something)). A to, co dzieje się tutaj, polega na tym, że pierwsze wyrażenie wyrażenia send (wartość, która ma być wysłana) jest oceniane najpierw - czyli <-b. Ale to będzie blokować zawsze (bo nikt nie wysyła nic na b), więc: tworzą

fatal error: all goroutines are asleep - deadlock!

Stosowna Sekcja Spec: Select statements:

Execution of a "select" statement proceeds in several steps:

  1. For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement. The result is a set of channels to receive from or send to, and the corresponding values to send. Any side effects in that evaluation will occur irrespective of which (if any) communication operation is selected to proceed. Expressions on the left-hand side of a RecvStmt with a short variable declaration or assignment are not yet evaluated.

  2. If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the "select" statement blocks until at least one of the communications can proceed.

  3. ...

Więc jeśli default jest obecny, select ma zapobiec blokowaniu, jeżeli żaden komunikacji można kontynuować w step 2, ale twój kod utknie w kroku 1.


Wystarczy być kompletne, jeśli nie byłoby goroutine że wyśle ​​wartość na b, to ocena <- b nie będzie blokować, więc wykonanie select nie tkwi w kroku 2, a ty ujrzy oczekiwanego "select worked as naively expected" (bo otrzymaniu od a nie może jeszcze przystąpić zatem default będzie wybrany):

go func() { b <- 1 }() 

select { 
    // ... 
} 

Wypróbuj na Go Playground.