2012-06-11 16 views
11

विचार है कि स्लाइस में चैनलों की एक चर संख्या है, उनके द्वारा प्राप्त प्रत्येक मान को एक चैनल में दबाएं, और इनपुट चैनल बंद होने के बाद इस आउटपुट चैनल को बंद करें। कुछ इस तरह, लेकिन दो से अधिक चैनलों के एक नंबर के लिए:क्या कई चैनलों को एक में मल्टीप्लेक्स करना संभव है?

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 
     } 
    } 
} 

ऊपर कोड व्यस्त पाशन से बचा जाता है के बाद से कोई default मामला है, जो अच्छा है (संपादित करें: यह की उपस्थिति की तरह ", ठीक है" लग रहा है चुनिंदा बयान गैर-अवरुद्ध करता है और लूप सभी के बाद व्यस्त होता है। लेकिन उदाहरण के लिए, कोड के बारे में सोचें जैसे यह ब्लॉक होगा)। इनपुट चैनलों की मनमानी संख्या के साथ समान प्रकार की कार्यक्षमता भी हासिल की जा सकती है? जाहिर है, यह स्लाइस जोड़ी को एक चैनल में कम करके किया जा सकता है, लेकिन यदि संभव हो, तो मैं एक सरल समाधान में अधिक रुचि रखूंगा।

उत्तर

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

अब फ़ंक्शन वाला एक पैकेज है (https://godoc.org/github.com/eapache/channels#Multiplex) जो कई goroutines के बजाय प्रतिबिंब का उपयोग कर समस्या हल करता है। – 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 
     } 
    } 
} 

संपादित करें: संदर्भ:

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

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

+0

दस्तावेज़ कहते हैं कि यदि आप ", ठीक" वाले चैनल से कोई मान पढ़ते हैं, तो ऑपरेशन ब्लॉक नहीं होता है। 'ओके' का मान तब 'झूठा' है और निष्पादन जारी है।यदि यह सही है (मैं जाने के लिए नया हूं और काफी नहीं बता सकता), तो यदि चैनल खाली है लेकिन अभी तक बंद नहीं हुआ है, तो 'अगर ठीक है' लाइन 'झूठी' के रूप में मूल्यांकन करेगी और 'else' शाखा निष्पादित करेगी। लेकिन यदि आप "v, ok: = <- src" और 'if' को एक चुनिंदा कथन के साथ बदल देंगे, तो यह काम कर सकता है। इसका परीक्षण करने के लिए मिला। आपके उत्तर के लिए धन्यवाद, बीटीडब्ल्यू। – elpres

+1

आपने कहां पढ़ा कि ऑपरेशन ब्लॉक नहीं करता है? मुझे यह नहीं मिला और यह मेरे द्वारा देखे जाने वाले मेल से प्रतीत नहीं होता है। मैंने दस्तावेज़ से पढ़ा है कि चैनल बंद होने के बाद * यह ब्लॉक नहीं होता है *। –

+1

ऐसा लगता है कि यह spec के पुराने संस्करण से आया है, उदा। [यहां] (http://go.googlecode.com/hg/doc/go_spec.html?r=c64e293#Communication_operators), "विधि अभिव्यक्ति" से पहले अंतिम पैराग्राफ पर नज़र डालें। वर्तमान संस्करण में यह मार्ग थोड़ा बदल गया है, और कहता है कि "एक शून्य मान लौटाया गया क्योंकि चैनल _closed और खाली_ (झूठा)" है। ऐसा लगता है कि 'झूठी' चैनलों को निकालने और बंद होने के बाद ही लौटाया जाता है, है ना? इसका मतलब है कि मैं गलत हूँ। – elpres

2

संपादित करें: जोड़ो में कमी उदाहरण कोड और जवाब की पुनर्क्रमित भागों गयी।

पसंदीदा समाधान "पुनर्गठन का गैर-जवाब है ताकि आपके पास चैनलों का टुकड़ा न हो।" पुनर्गठन अक्सर इस सुविधा का उपयोग कर सकता है कि एकाधिक goroutines एक चैनल को भेज सकते हैं। तो आपके प्रत्येक स्रोत को अलग-अलग चैनलों पर भेजने और फिर चैनलों के समूह से प्राप्त करने के बजाय, केवल एक चैनल बनाएं और सभी स्रोतों को उस चैनल पर भेजने दें।

गो चैनलों के टुकड़े से प्राप्त करने के लिए एक सुविधा प्रदान नहीं करता है। यह अक्सर पूछे जाने वाले प्रश्न है, और जब दिए गए समाधान को प्राथमिकता दी जाती है, तो इसे प्रोग्राम करने के तरीके हैं। एक समाधान मैंने सोचा था कि आप अपने मूल प्रश्न में "स्लाइस जोड़ी को कम करने" कहकर सुझाव दे रहे थे कि एक द्विआधारी विभाजन है और समाधान जीत गया है। यह ठीक काम करता है, जब तक आपके पास दो चैनलों को मल्टीप्लेक्स करने का समाधान होता है। इसके लिए आपका उदाहरण कोड काम करने के बहुत करीब है।

आप अपना उदाहरण कोड काम करने के लिए बस एक छोटी सी चाल खो रहे हैं। जहां आप कमी करते हैं, चैनल वैरिएबल को शून्य पर सेट करने के लिए एक लाइन जोड़ें। उदाहरण के लिए, मैंने कोड को

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) के साथ फ़ंक्शन के रूप में ढूंढ रहा हूं, यानी, जो कि दो को हार्ड-कोड किए जाने के बजाय इनपुट चैनलों की मनमानी संख्या पर काम कर सकता है। – elpres