2012-06-11 2 views
11

아이디어는 슬라이스에 가변 개수의 채널을 가지며이를 통해 수신 된 각 값을 단일 채널로 푸시하고 마지막 입력 채널이 닫히면이 출력 채널을 닫는 것입니다. 이런 식으로 뭔가하지만, 더 둘 이상의 채널의 번호 : 좋은 더 default 경우가 없기 때문에여러 채널을 하나로 멀티 플렉스 할 수 있습니까?

func multiplex(cin1, cin2, cout chan int) { 
    n := 2 
    for { 
     select { 
     case v, ok := <-cin1: 
      if ok { 
       cout <- v 
      } else { 
       n -= 1 
      } 

     case v, ok := <-cin2: 
      if ok { 
       cout <- v 
      } else { 
       n -= 1 
      } 
     } 

     if n == 0 { 
      close(cout) 
      break 
     } 
    } 
} 

위의 코드는 바쁜 루프를 방지 (편집 : 그것은 "확인"의 존재처럼 보인다 select 문을 비 차단으로 만들고 루프는 결국 바쁘다.하지만 예제에서는 코드를 차단하는 것처럼 생각한다. 임의의 수의 입력 채널을 사용하여 동일한 종류의 기능을 구현할 수 있습니까? 분명히, 이것은 슬라이스를 한 쌍의 채널로 줄임으로써 가능할 수 있지만 가능한 한 더 간단한 해결책에 더 관심이 있습니다.

답변

24

나는이 조각은 당신이 무엇을 찾고 있는지 않는 생각합니다. 필자는 서명을 변경하여 입력 및 출력이 한 방향으로 만 통신에 사용되어야 함을 분명히했습니다. sync.WaitGroup이 추가되었으므로 모든 입력이 완료되었음을 알리는 신호가 필요합니다. 이는 매우 쉽습니다.

func combine(inputs []<-chan int, output chan<- int) { 
    var group sync.WaitGroup 
    for i := range inputs { 
    group.Add(1) 
    go func(input <-chan int) { 
     for val := range input { 
     output <- val 
     } 
     group.Done() 
    } (inputs[i]) 
    } 
    go func() { 
    group.Wait() 
    close(output) 
    }() 
} 
+1

아, 아주 좋은 해결책, 명확하고 간결. 고맙습니다! – elpres

+0

여러 goroutine 대신 리플렉션을 사용하여 문제를 해결하는 기능 (https://godoc.org/github.com/eapache/channels#Multiplex)이 포함 된 패키지가 있습니다. – Evan

0

나는 goroutines를 사용하여 이것을 만들었습니다. 그것은 당신이 원하는 것입니까?

package main 

import (
    "fmt" 
) 

func multiplex(cin []chan int, cout chan int) { 
    n := len(cin) 
    for _, ch := range cin { 
     go func(src chan int) { 
      for { 
       v, ok := <-src 
       if ok { 
        cout <- v 
       } else { 
        n-- // a little dangerous. Maybe use a channel to avoid missed decrements 
        if n == 0 { 
         close(cout) 
        } 
        break 
       } 
      } 
     }(ch) 
    } 
} 

// a main to test the multiplex 
func main() { 
    cin := make([]chan int, 3) 
    cin[0] = make(chan int, 2) 
    cin[1] = make(chan int, 2) 
    cin[2] = make(chan int, 2) 
    cout := make(chan int, 2) 
    multiplex(cin, cout) 
    cin[1] <- 1 
    cin[0] <- 2 
    cin[2] <- 3 
    cin[1] <- 4 
    cin[0] <- 5 
    close(cin[1]) 
    close(cin[0]) 
    close(cin[2]) 
    for { 
     v, ok := <-cout 
     if ok { 
      fmt.Println(v) 
     } else { 
      break 
     } 
    } 
} 

EDIT : 참고 문헌 :

http://golang.org/ref/spec#Receive_operator

http://golang.org/ref/spec#Close

+0

"ok"를 사용하여 채널에서 값을 읽으면 작업이 차단되지 않는다고 말합니다. 'ok'의 값은 간단히'false'이고 실행은 계속됩니다.이것이 정확하다면 (Go to new이고 꽤 말할 수 없다) 채널이 비어 있지만 아직 닫혀 있지 않다면'if ok' 라인은'false'로 평가되고'else' 브랜치를 실행합니다. 그러나 "v, ok : = <- src"및'if'를 select 문으로 대체하면 작동 할 수 있습니다. 그걸 시험 해봐. 답장을 보내 주셔서 감사합니다. – elpres

+1

작업이 차단되지 않은 것을 어디에서 읽었습니까? 나는 그것을 발견하지 못하고 내가 관찰 한 것과 일치하지 않는 것처럼 보인다. 의사가 채널을 닫으면 차단하지 않습니다. –

+1

예전 버전의 것 같습니다. [여기] (http://go.googlecode.com/hg/doc/go_spec.html?r=c64e293#Communication_operators)에서 "메소드 표현식"앞의 마지막 단락을 살펴보십시오. 현재 버전에서는이 구절이 약간 변경되어 "채널이 닫혀 있고 값이 false (false)"이기 때문에 0 값이 반환됩니다. 이것은'false'처럼 들리지만 채널이 흘러 나오고 닫힌 후에 만 ​​반환됩니다. 맞습니까? 그 말은 내가 잘못 본 것입니다. – elpres

2

편집 : 페어 환원 예 코드 및 응답의 재정렬 부를 첨가.

선호하는 솔루션은 "채널 구조가 없도록 재구성"입니다. 구조 조정은 종종 여러 goroutine이 단일 채널로 보낼 수있는 기능을 사용할 수 있습니다. 따라서 각 소스를 개별 채널로 보내고 여러 채널에서 수신하는 대신 하나의 채널을 만들고 모든 소스에서 해당 채널을 보내도록하십시오.

이동은 채널 조각에서 수신하는 기능을 제공하지 않습니다. 그것은 자주 묻는 질문이며, 방금 주어진 해결책이 선호되는 반면, 그것을 프로그램하는 방법이 있습니다. 당신이 원래의 질문에서 "슬라이스 쌍을 줄이는 것"이라고 말하는 것으로 생각한 해결책은 이진 나누기와 정복 해결책입니다. 두 개의 채널을 하나로 멀티플렉싱하는 솔루션을 사용하는 한 잘 작동합니다. 이에 대한 예제 코드는 작업에 매우 가깝습니다.

예제 코드를 작동시키기위한 간단한 방법이 하나 빠져 있습니다. n을 감소시킬 때 채널 변수를 nil로 설정하는 행을 추가하십시오. 예를 들어, 코드를 읽게했습니다.

case v, ok := <-cin1: 
     if ok { 
      cout <- v 
     } else { 
      n-- 
      cin1 = nil 
     } 
    case v, ok := <-cin2: 
     if ok { 
      cout <- v 
     } else { 
      n-- 
      cin2 = nil 
     } 
    } 

이 솔루션은 원하는 것을 처리하고 대기 상태가되지 않습니다. 그럼

, 조각을 다중화하는 기능으로이 솔루션을 포함하는 전체 예제 :

package main 

import (
    "fmt" 
    "time" 
) 

func multiplex(cin []chan int, cout chan int) { 
    var cin0, cin1 chan int 
    switch len(cin) { 
    case 2: 
     cin1 = cin[1] 
     fallthrough 
    case 1: 
     cin0 = cin[0] 
    case 0: 
    default: 
     cin0 = make(chan int) 
     cin1 = make(chan int) 
     half := len(cin)/2 
     go multiplex(cin[:half], cin0) 
     go multiplex(cin[half:], cin1) 
    } 
    for cin0 != nil || cin1 != nil { 
     select { 
     case v, ok := <-cin0: 
      if ok { 
       cout <- v 
      } else { 
       cin0 = nil 
      } 
     case v, ok := <-cin1: 
      if ok { 
       cout <- v 
      } else { 
       cin1 = nil 
      } 
     } 
    } 
    close(cout) 
} 

func main() { 
    cin := []chan int{ 
     make(chan int), 
     make(chan int), 
     make(chan int), 
    } 
    cout := make(chan int) 
    for i, c := range cin { 
     go func(x int, cx chan int) { 
      for i := 1; i <= 3; i++ { 
       time.Sleep(100 * time.Millisecond) 
       cx <- x*10 + i 
      } 
      close(cx) 
     }(i, c) 
    } 
    go multiplex(cin, cout) 
    for { 
     select { 
     case v, ok := <-cout: 
      if ok { 
       fmt.Println("main gets", v) 
      } else { 
       return 
      } 
     } 
    } 
} 
+1

아니요. 내가 'func multiplex (cin [] chan int, cout chan int)'라는 서명으로 함수를 찾고 있는데, 즉 2로 하드 코딩되지 않고 임의의 수의 입력 채널에서 작동 할 수있는 함수입니다. – elpres

관련 문제