2016-12-16 5 views
1

프로젝트 용 동적 버퍼가있는 비 차단 채널이 필요하므로이 코드를 작성했습니다. 여기 Go에서 선택 문의 외부 조건을 이동할 때이 교착 상태가 발생하는 이유

는 유형 선언입니다 :

//receiver is the receiver of the non blocking channel 
type receiver struct { 
    Chan <-chan string 
    list *[]string 
    mutex sync.RWMutex 
} 

//Clear sets the buffer to 0 elements 
func (r *receiver) Clear() { 
    r.mutex.Lock() 
    *r.list = (*r.list)[:0] 
    r.mutex.Unlock() 
    //Discards residual content 
    if len(r.Chan) == 1 { 
     <-r.Chan 
    } 
} 

생성자 :

//NewNonBlockingChannel returns the receiver & sender of a non blocking channel 
func NewNonBlockingChannel() (*receiver, chan<- string) { 
    //Creates the send and receiver channels and the buffer 
    send := make(chan string) 
    recv := make(chan string, 1) 
    list := make([]string, 0, 20) 

    r := &receiver{Chan: recv, list: &list} 

    go func() { 

     for { 
      //When the receiver is empty sends the next element from the buffer 
      if len(recv) == 0 && len(list) > 0 { 
       r.mutex.Lock() 
       recv <- list[len(list)-1] 
       list = list[:len(list)-1] 
       r.mutex.Unlock() 
      } 

      select { 
      //Adds the incoming elements to the buffer 
      case s := <-send: 
       r.mutex.Lock() 
       list = append(list, s) 
       r.mutex.Unlock() 
       //default: 

      } 
     } 
    }() 

    return r, send 
} 

그리고 메인의 장난감 시험 :

func main() { 
    recv, sender := NewNonBlockingChannel() 

    //send data to the channel 
    go func() { 
     for i := 0; i < 5; i++ { 
      sender <- "Hi" 
     } 
     time.Sleep(time.Second) 
     for i := 0; i < 5; i++ { 
      sender <- "Bye" 
     } 
    }() 
    time.Sleep(time.Millisecond * 70) //waits to receive every "Hi" 
    recv.Clear() 
    for data := range recv.Chan { 
     println(data) 
    } 

} 

나는 그것을 테스트하고 교착 상태가에서 일어난 보낸 사람으로부터받은 선택문 "사례 : = < 보내기 - : 나는 선택 문 모두에 다음 버퍼링 된 문자열을 전송하는 조건 블록을 이동할 때는 "하지만 완벽하게 작동합니다 :

go func() { 

    for { 
     select { 
     //Adds the incoming elements to the buffer 
     case s := <-send: 
      r.mutex.Lock() 
      list = append(list, s) 
      r.mutex.Unlock() 
     default: 
      //When the receiver is empty sends the next element from the buffer 
      if len(recv) == 0 && len(list) > 0 { 
       r.mutex.Lock() 
       recv <- list[len(list)-1] 
       list = list[:len(list)-1] 
       r.mutex.Unlock() 
      } 
     } 
    } 
}() 

나는 이유를 알고 싶습니다.

+0

관용적 인 Go를 원하면 채널과 잠금을 혼합하지 않아야합니다. * 공유 메모리로 통신하지 마십시오. 대신 통신을 통해 메모리를 공유하십시오. * https://blog.golang.org/share-memory-by-communicating –

+0

하지만 슬라이스에 액세스하는 동안 스레드 안전을 위해 잠금 장치가 필요합니다. 서비스의 특성 때문에 비 차단 채널이 필요하므로 슬라이스를 데이터 버퍼로 사용하기로 결정했습니다. –

답변

0

예를 들어,이 시나리오를 상상해 :

"안녕"이 세 가지를하기 때문이다 ( list에 남아있다

for data := range recv.Chan { 
     println(data) 
} 

세에 의해 주, 그들 중 두 사람이었다 인쇄에 의해 전송 된

오 시간이 for 루프를 입력 단지)

및에 의해 시작된 goroutine을 list = append(list, s) 세 번 case s := <-send을 충족하고, 할 수 있도록 조건 len(recv) == 0 && len(list) > 0는 거짓은 s := <-send 수당을 기다리고 있습니다.

대기 gorountine이 일어날 것이고, 조건이 :받는 당신이 select에 코드를

if len(recv) == 0 && len(list) > 0 { 
    r.mutex.Lock() 
    recv <- list[len(list)-1] 
    list = list[:len(list)-1] 
    r.mutex.Unlock() 
} 

를 이동할 때 영원히

을 기다릴 것이다, 그래서 더 이상 직원이 될 상황이 다르지 않습니다 if len(recv) == 0 && len(list) > 0은 정시에 맞아야합니다. 일이 잘 풀립니다.