단일 선택 사례에서 채널 운영 연결: 잠재적 위험 분석
Go에서 select 문은 다중화에 편리한 방법을 제공합니다. 채널. 다음 시나리오를 생각해 보십시오. 서로 다른 간격으로 메시지를 전송하는 두 개의 채널 A와 B가 있습니다. 우리는 A와 B 모두로부터 메시지를 받는 팬인 채널을 만들고 싶습니다.
아래 코드는 이를 보여줍니다.
func fanIn(input1, input2 <-chan string) <-chan string { ch := make(chan string) go func() { for { select { case t := <-input1: ch <- t case t := <-input2: ch <- t } } }() return ch }
이 코드를 실행하면 메시지를 받을 것으로 예상됩니다. 인터리브 방식으로 두 채널 모두에서 전송됩니다. 그러나 Select Case 문을 다음과 같이 수정하면 예기치 않은 동작이 발생합니다.
func fanIn(input1, input2 <-chan string) <-chan string { ch := make(chan string) go func() { for { select { case ch <- <-input1: case ch <- <-input2: } } }() return ch }
이 경우 일부 메시지는 올바르게 수신되지만 값이 삭제되고 결국 교착 상태가 발생합니다. 이 동작의 이유는 select의 기본 작동에 있습니다.
select에서는 하나의 채널 읽기 또는 쓰기 작업만 비차단입니다. 다른 모든 작업은 정상적으로 작동합니다. 수정된 코드에서는 두 경우 모두 비차단 채널 쓰기가 포함되어 있습니다. 이로 인해 입력 채널의 메시지가 대기열에 추가되지만 팬인 채널은 한 번에 하나만 사용할 수 있는 상황이 발생합니다.
결과적으로 팬인 시 메시지가 삭제되고 교착 상태가 발생할 수 있습니다. 채널에는 작성자가 없으며 독자는 더 많은 값을 기다리고 있습니다.
이 문제를 방지하려면 select 문에서 하나의 작업만 비차단이어야 한다는 점을 이해하는 것이 중요합니다. 단일 선택 사례에서 여러 채널 작업을 수행해야 하는 경우 다음과 같은 비차단 선택 도우미 기능 사용을 고려하세요.
func nonBlockingSelect(cases []reflect.SelectCase) (chosen int, recv interface{}, ok bool) { for i, c := range cases { if c.Dir == reflect.SelectSend && c.Chan == nil { continue } v, ok := reflect.Select(cases) return v.Index, v.Elem().Interface(), ok } return -1, nil, false }
그런 다음 수정된 팬인 기능을 다음과 같이 다시 작성할 수 있습니다.
func fanIn(input1, input2 <-chan string) <-chan string { ch := make(chan string) go func() { for { select { case c1 := <-input1: nonBlockingSelect([]reflect.SelectCase{ {Dir: reflect.SelectSend, Chan: reflect.ValueOf(ch), Send: reflect.ValueOf(c1)}, }) case c2 := <-input2: nonBlockingSelect([]reflect.SelectCase{ {Dir: reflect.SelectSend, Chan: reflect.ValueOf(ch), Send: reflect.ValueOf(c2)}, }) } } }() return ch }
비차단 선택 도우미를 사용하면 하나의 채널 작업만 비차단으로 보장되어 값 삭제 문제를 방지하고 교착상태.
위 내용은 Go의 `select` 문에서 연결된 채널 작업이 어떻게 교착 상태 및 데이터 손실로 이어질 수 있나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!