하나의 채널에서 청취하는 여러 고 루틴
동일한 채널에서 동시에 수신하려는 여러 고 루틴이 있습니다. 채널에서 수신을 시작하는 마지막 고 루틴이 값을 얻는 것 같습니다. 이것은 언어 사양의 어딘가에 있습니까 아니면 정의되지 않은 동작입니까?
c := make(chan string)
for i := 0; i < 5; i++ {
go func(i int) {
<-c
c <- fmt.Sprintf("goroutine %d", i)
}(i)
}
c <- "hi"
fmt.Println(<-c)
산출:
goroutine 4
편집하다:
생각보다 복잡하다는 걸 방금 깨달았습니다. 메시지는 모든 고 루틴 주위에 전달됩니다.
c := make(chan string)
for i := 0; i < 5; i++ {
go func(i int) {
msg := <-c
c <- fmt.Sprintf("%s, hi from %d", msg, i)
}(i)
}
c <- "original"
fmt.Println(<-c)
산출:
original, hi from 0, hi from 1, hi from 2, hi from 3, hi from 4
예, 복잡합니다.하지만 상황을 훨씬 더 간단하게 만들어주는 몇 가지 경험 법칙이 있습니다.
- 글로벌 범위의 채널에 액세스하는 대신 go-routine에 전달 하는 채널에 대해 형식 인수를 사용하는 것을 선호합니다 . 이 방법으로 더 많은 컴파일러 검사를 얻을 수 있으며 모듈화도 향상됩니다.
- 특정 go-routine ( 'main'포함)의 동일한 채널에서 읽기와 쓰기를 모두 피하십시오 . 그렇지 않으면 교착 상태가 훨씬 더 위험합니다.
다음은이 두 가지 지침을 적용하는 프로그램의 대체 버전입니다. 이 사례는 채널에서 많은 작가와 한 명의 독자를 보여줍니다.
c := make(chan string)
for i := 1; i <= 5; i++ {
go func(i int, co chan<- string) {
for j := 1; j <= 5; j++ {
co <- fmt.Sprintf("hi from %d.%d", i, j)
}
}(i, c)
}
for i := 1; i <= 25; i++ {
fmt.Println(<-c)
}
http://play.golang.org/p/quQn7xePLw
단일 채널에 기록하는 5 개의 go-routine을 생성하며 각각 5 번 기록합니다. 기본 go-routine은 25 개의 메시지를 모두 읽습니다. 표시되는 순서가 종종 순차적이지 않음을 알 수 있습니다 (즉, 동시성이 분명함).
이 예는 Go 채널의 기능을 보여줍니다. 여러 작성자가 하나의 채널을 공유 할 수 있습니다. Go는 메시지를 자동으로 인터리브합니다.
두 번째 예에서 볼 수 있듯이 한 채널에있는 한 명의 작성자와 여러 명의 독자에 대해서도 동일하게 적용됩니다.
c := make(chan int)
var w sync.WaitGroup
w.Add(5)
for i := 1; i <= 5; i++ {
go func(i int, ci <-chan int) {
j := 1
for v := range ci {
time.Sleep(time.Millisecond)
fmt.Printf("%d.%d got %d\n", i, j, v)
j += 1
}
w.Done()
}(i, c)
}
for i := 1; i <= 25; i++ {
c <- i
}
close(c)
w.Wait()
이 두 번째 예 에는 메인 고 루틴에 부과 된 대기가 포함되어 있습니다. 그렇지 않으면 즉시 종료되고 다른 5 개의 고 루틴이 조기에 종료됩니다 ( 이 수정에 대한 olov 덕분에 ) .
두 예 모두 버퍼링이 필요하지 않았습니다. 일반적으로 버퍼링을 성능 향상기로 만 보는 것이 좋은 원칙입니다. 프로그램이 교착하지 않는 경우 없이 버퍼, 그것은 교착하지 않습니다 과 중 버퍼 (하지만 그 반대는 아니다 항상 true). 따라서 또 다른 경험 규칙으로 버퍼링없이 시작한 다음 필요에 따라 나중에 추가하십시오 .
늦게 답장을 드렸지만 장래 설문 조사, "글로벌"버튼, 모두에게 브로드 캐스트 와 같은 향후 다른 사람들에게 도움이되기를 바랍니다 .
Effective Go 는 문제를 설명합니다.
수신자는 수신 할 데이터가있을 때까지 항상 차단합니다.
즉, 하나의 채널을 청취하는 고 루틴이 하나 이상있을 수 없으며 모든 고 루틴이 동일한 값을받을 것으로 기대합니다.
이 코드 예제를 실행하십시오 .
package main
import "fmt"
func main() {
c := make(chan int)
for i := 1; i <= 5; i++ {
go func(i int) {
for v := range c {
fmt.Printf("count %d from goroutine #%d\n", v, i)
}
}(i)
}
for i := 1; i <= 25; i++ {
c<-i
}
close(c)
}
채널을 듣고있는 고 루틴이 5 개 있어도 "카운트 1"이 두 번 이상 표시되지 않습니다. 이는 첫 번째 고 루틴이 채널을 차단할 때 다른 모든 고 루틴이 줄을서야하기 때문입니다. 채널이 차단 해제되면 카운트가 이미 수신되어 채널에서 제거되어 라인의 다음 고 루틴이 다음 카운트 값을 얻습니다.
복잡하다.
또한 GOMAXPROCS = NumCPU+1
. 예를 들면
package main
import (
"fmt"
"runtime"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU() + 1)
fmt.Print(runtime.GOMAXPROCS(0))
c := make(chan string)
for i := 0; i < 5; i++ {
go func(i int) {
msg := <-c
c <- fmt.Sprintf("%s, hi from %d", msg, i)
}(i)
}
c <- ", original"
fmt.Println(<-c)
}
산출:
5, original, hi from 0, hi from 4
그리고 버퍼링 된 채널에서 어떤 일이 발생하는지 확인하십시오. 예를 들면
package main
import "fmt"
func main() {
c := make(chan string, 5+1)
for i := 0; i < 5; i++ {
go func(i int) {
msg := <-c
c <- fmt.Sprintf("%s, hi from %d", msg, i)
}(i)
}
c <- "original"
fmt.Println(<-c)
}
산출:
original
이러한 경우도 설명 할 수 있어야합니다.
기존 솔루션을 연구하고 간단한 방송 라이브러리 https://github.com/grafov/bcast를 만들었습니다 .
group := bcast.NewGroup() // you created the broadcast group
go bcast.Broadcasting(0) // the group accepts messages and broadcast it to all members
member := group.Join() // then you join member(s) from other goroutine(s)
member.Send("test message") // or send messages of any type to the group
member1 := group.Join() // then you join member(s) from other goroutine(s)
val := member1.Recv() // and for example listen for messages
여러 고 루틴의 경우 하나의 채널에서 청취 할 수 있습니다. 핵심은 메시지 자체입니다. 다음과 같은 메시지를 정의 할 수 있습니다.
package main
import (
"fmt"
"sync"
)
type obj struct {
msg string
receiver int
}
func main() {
ch := make(chan *obj) // both block or non-block are ok
var wg sync.WaitGroup
receiver := 25 // specify receiver count
sender := func() {
o := &obj {
msg: "hello everyone!",
receiver: receiver,
}
ch <- o
}
recv := func(idx int) {
defer wg.Done()
o := <-ch
fmt.Printf("%d received at %d\n", idx, o.receiver)
o.receiver--
if o.receiver > 0 {
ch <- o // forward to others
} else {
fmt.Printf("last receiver: %d\n", idx)
}
}
go sender()
for i:=0; i<reciever; i++ {
wg.Add(1)
go recv(i)
}
wg.Wait()
}
출력은 무작위입니다.
5 received at 25
24 received at 24
6 received at 23
7 received at 22
8 received at 21
9 received at 20
10 received at 19
11 received at 18
12 received at 17
13 received at 16
14 received at 15
15 received at 14
16 received at 13
17 received at 12
18 received at 11
19 received at 10
20 received at 9
21 received at 8
22 received at 7
23 received at 6
2 received at 5
0 received at 4
1 received at 3
3 received at 2
4 received at 1
last receiver 4
참고 URL : https://stackoverflow.com/questions/15715605/multiple-goroutines-listening-on-one-channel
'developer tip' 카테고리의 다른 글
CFLAGS, CCFLAGS, CXXFLAGS-이 변수가 정확히 무엇을 제어합니까? (0) | 2020.11.21 |
---|---|
스콜 렘은 무엇입니까? (0) | 2020.11.21 |
HTML의 뷰포트 란 무엇입니까? (0) | 2020.11.21 |
메모리 오류 및 목록 제한? (0) | 2020.11.21 |
Int32.ToString () 문화권에 따라 다르나요? (0) | 2020.11.21 |