2012-06-20 14 views
20

मैं दो चैनलों पर नियमित रूप से सुनना चाहता हूं, जब दोनों चैनलों को हटा दिया जाता है। हालांकि, यदि दोनों चैनलों में डेटा होता है, तो मैं चाहता हूं कि दूसरे को संभालने से पहले एक को निकाला जाए।गो चुनिंदा कथन में प्राथमिकता

नीचे दिए गए कामकाजी उदाहरण में मैं exit से पहले निकाले जाने के लिए सभी out की कामना करता हूं। मैं select -स्टेटमेंट का उपयोग करता हूं जिसमें कोई प्राथमिकता आदेश नहीं है। मैं समस्या के आसपास कैसे हो सकता हूं, बाहर निकलने से पहले सभी 10 आउट-मानों को संभाला जा सकता है?

package main 

import "fmt" 

func sender(out chan int, exit chan bool){ 
    for i := 1; i <= 10; i++ { 
     out <- i 
    } 
    exit <- true 
} 

func main(){ 
    out := make(chan int, 10) 
    exit := make(chan bool) 

    go sender(out, exit) 

    L: 
    for { 
     select { 
      case i := <-out: 
       fmt.Printf("Value: %d\n", i) 
      case <-exit: 
       fmt.Println("Exiting") 
       break L 
     } 
    } 
    fmt.Println("Did we get all 10? Most likely not") 
} 

उत्तर

14
package main 

import "fmt" 

func sender(out chan int, exit chan bool) { 
    for i := 1; i <= 10; i++ { 
     out <- i 
    } 
    exit <- true 
} 

func main() { 
    out := make(chan int, 10) 
    exit := make(chan bool) 

    go sender(out, exit) 

    for { 
     select { 
     case i := <-out: 
      fmt.Printf("Value: %d\n", i) 
      continue 
     default: 
     } 
     select { 
     case i := <-out: 
      fmt.Printf("Value: %d\n", i) 
      continue 
     case <-exit: 
      fmt.Println("Exiting") 
     } 
     break 
    } 
    fmt.Println("Did we get all 10? I think so!") 
} 

पहले चयन का डिफ़ॉल्ट मामला इसे अवरुद्ध करता है। चयन बाहर निकलने वाले चैनल को देखे बिना आउट चैनल को निकाल देगा, लेकिन अन्यथा इंतजार नहीं करेगा। यदि आउट चैनल खाली है, तो यह तुरंत दूसरे चयन में गिर जाता है। दूसरा चयन अवरुद्ध है। यह किसी भी चैनल पर डेटा की प्रतीक्षा करेगा। यदि कोई निकास आता है, तो यह इसे संभालता है और लूप को बाहर निकलने की अनुमति देता है। यदि डेटा आता है, तो यह लूप के ऊपर और वापस नाली मोड में वापस चला जाता है।

+1

विचार मेरे जैसा ही है। लेकिन सच है, 'जारी रखें' कथन के साथ, आप ध्वज की आवश्यकता से छुटकारा पा सकते हैं। होशियार। खैर, यह शायद एक अच्छा जवाब है जैसा कि मैं प्राप्त कर सकता हूं। धन्यवाद! – ANisus

+2

आउट चैनल बंद होने पर यह पहले चयन कथन में असीमित रूप से लूप होगा। – jorelli

+1

जॉरेली, काफी सच है। यदि आप अप्रत्याशित रूप से चैनल को बंद करने वाले शत्रुतापूर्ण या छोटी गाड़ी गोरोटाइन्स की अनुमति देना चाहते हैं, तो आप प्राप्त होने पर ठीक स्थिति की जांच करेंगे। – Sonia

5

एक और दृष्टिकोण:

package main 

import "fmt" 

func sender(c chan int) chan int { 
     go func() { 
       for i := 1; i <= 15; i++ { 
         c <- i 
       } 
       close(c) 
     }() 
     return c 
} 

func main() { 
     for i := range sender(make(chan int, 10)) { 
       fmt.Printf("Value: %d\n", i) 
     } 
     fmt.Println("Did we get all 15? Surely yes") 
} 

$ go run main.go 
Value: 1 
Value: 2 
Value: 3 
Value: 4 
Value: 5 
Value: 6 
Value: 7 
Value: 8 
Value: 9 
Value: 10 
Value: 11 
Value: 12 
Value: 13 
Value: 14 
Value: 15 
Did we get all 15? Surely yes 
$ 
+1

सुझाव के लिए धन्यवाद! अगर मैं आपको सही तरीके से समझता हूं, तो आप चैनल को बंद करके बाहर निकलने के लिए केवल एक चैनल का उपयोग करने का सुझाव देते हैं, इस प्रकार 'श्रेणी के लिए 'कथन को तोड़ते हैं। सच है, शायद यह करने का एक बेहतर तरीका है, लेकिन मेरे मामले में मैं दो चैनलों के साथ काम कर रहा हूं। – ANisus

1

मैं एक साधारण तरीके को बनाया है। यह जो मैं चाहता है, लेकिन किसी और एक बेहतर समाधान है, तो कृपया मुझे बताएं:

exiting := false 
for !exiting || len(out)>0 { 
    select { 
     case i := <-out: 
      fmt.Printf("Value: %d\n", i) 
     case <-exit: 
      exiting = true 
      fmt.Println("Exiting") 
    } 
} 

प्राप्त करने पर बाहर निकलने के बजाय, मैं एक निकास झंडा, यकीन है कि कुछ भी नहीं है एक बार मैं कर दिया है बाहर निकलने chan out में छोड़ दिया है ।

+1

यह काम करता है और अच्छा और कॉम्पैक्ट है, लेकिन सामान्य रूप से बचने के लिए आपको कुछ युक्तियों का उपयोग करना चाहिए। झंडे भ्रमित हो जाते हैं क्योंकि कार्यक्रम बड़े हो जाते हैं। वे गेटोस की तरह हैं। अधिक गंभीरता से, लेन (चैन) अक्सर दौड़ पेश कर सकते हैं। यह इस स्थिति में ठीक दिखता है, लेकिन कई मामलों में लेन (चैन) के आधार पर निर्णय लेने के लिए अमान्य है क्योंकि यह कार्रवाई करने से पहले इसे बदल सकता है। उस मामले की कल्पना करें जहां आपको लेन == 0 मिलता है, फिर एक मूल्य आता है, फिर बाहर निकलता है, और बाहर निकलने का चयन करता है। आप चिल्ला सकते हैं और कह सकते हैं कि वे एक ही समय में पहुंचे, लेकिन कुछ समय के महत्वपूर्ण कार्यक्रमों में, इससे कोई फर्क नहीं पड़ता। – Sonia

+0

उम्म, शायद यह अभी भी मेरे द्वारा वर्णित मामले में काम करता है। क्षमा करें अगर यह एक बुरा उदाहरण है। लेकिन वैसे भी, मैं सिंक्रनाइज़ेशन कोड में लेन का उपयोग करने से बचने की कोशिश करता हूं। – Sonia

+0

हाय फिर सोनिया :)। अच्छा इनपुट हाँ, मेरे मामले में इससे कोई फर्क नहीं पड़ता। मैं बस बाहर निकलने से पहले क्या बाहर जा रहा था फ्लश करना चाहता था। हालांकि, मैं वास्तव में 'श्रेणी के लिए' और 'बंद (आउट) 'का उपयोग करके कोड को रेडिड करता हूं (जैसा कि जेएमएनएल द्वारा सुझाया गया है)। फिर बंद होने से पहले चैनल पाइप में रखे गए आउट-इवेंट केवल "फ्लश" होंगे। मैं लैन (चान) के आधार पर निर्णय लेने से बचूंगा अगर नास्डैक ने मुझे उनके लिए कुछ गो कार्यक्रम करने के लिए कहा है;) – ANisus

26

भाषा इस मूल रूप से समर्थन करती है और कोई कामकाज की आवश्यकता नहीं है। यह बहुत आसान है: छोड़ने वाले चैनल को केवल निर्माता को दिखाई देना चाहिए। छोड़ने पर, निर्माता चैनल बंद कर देता है। केवल तभी जब चैनल खाली और बंद हो जाता है तो उपभोक्ता छोड़ देता है।

v, ok := <-c 

इस का संकेत है या नहीं, मूल्य v वास्तव में किया गया था बंद चैनल (ok == true) को पढ़ने के लिए एक बूलियन ok सेट हो जाएगा, या अगर v करने के लिए सेट किया गया था: इस प्रकार इस चैनल से पढ़कर ही संभव बनाया है चैनल c द्वारा संचालित प्रकार के शून्य-मूल्य क्योंकि c बंद और खाली है (ok == false)। जब चैनल बंद हो जाता है और खाली नहीं होता है, v मान्य मान होगा और oktrue होगा। जब चैनल बंद और खाली होता है, v चैनल c द्वारा संचालित प्रकार का शून्य-मूल्य होगा, और okfalse होगा, जो दर्शाता है कि v बेकार है।

package main 

import (
    "fmt" 
    "math/rand" 
    "time" 
) 

var (
    produced = 0 
    processed = 0 
) 

func produceEndlessly(out chan int, quit chan bool) { 
    defer close(out) 
    for { 
     select { 
     case <-quit: 
      fmt.Println("RECV QUIT") 
      return 
     default: 
      out <- rand.Int() 
      time.Sleep(time.Duration(rand.Int63n(5e6))) 
      produced++ 
     } 
    } 
} 

func quitRandomly(quit chan bool) { 
    d := time.Duration(rand.Int63n(5e9)) 
    fmt.Println("SLEEP", d) 
    time.Sleep(d) 
    fmt.Println("SEND QUIT") 
    quit <- true 
} 

func main() { 
    vals, quit := make(chan int, 10), make(chan bool) 
    go produceEndlessly(vals, quit) 
    go quitRandomly(quit) 
    for { 
     x, ok := <-vals 
     if !ok { 
      break 
     } 
     fmt.Println(x) 
     processed++ 
     time.Sleep(time.Duration(rand.Int63n(5e8))) 
    } 
    fmt.Println("Produced:", produced) 
    fmt.Println("Processed:", processed) 
} 

इस जाने कल्पना की "ऑपरेटर प्राप्त करें" खंड में प्रलेखित है: http://golang.org/ref/spec#Receive_operator

+0

धन्यवाद यह वही समाधान है जिसे मैं ढूंढ रहा था, और इसमें संभावित दौड़ की स्थिति बग नहीं है जो सोनिया के जवाब में है – BrandonAGr

0

मेरे मामले में, मैं वास्तव में एक से डेटा को प्राथमिकता देना चाहता था

यहाँ वर्णन करने के लिए एक उदाहरण है एक दूसरे के ऊपर चैनल, और न केवल बाहर निकास संकेत बाहर है।एक ही मुद्दे के साथ किसी और के लाभ के लिए मुझे लगता है कि इस दृष्टिकोण संभावित रेस स्थिति के बिना काम करता है:

OUTER: 
for channelA != nil || channelB != nil { 

    select { 

    case typeA, ok := <-channelA: 
     if !ok { 
      channelA = nil 
      continue OUTER 
     } 
     doSomething(typeA) 

    case nodeIn, ok := <-channelB: 
     if !ok { 
      channelB = nil 
      continue OUTER 
     } 

     // Looped non-blocking nested select here checks that channelA 
     // really is drained before we deal with the data from channelB 
     NESTED: 
     for { 
      select { 
      case typeA, ok := <-channelA: 
       if !ok { 
        channelA = nil 
        continue NESTED 
       } 
       doSomething(typeA) 

      default: 
       // We are free to process the typeB data now 
       doSomethingElse(typeB) 
       break NESTED 
      } 
     } 
    } 

} 
0

मुझे लगता है कि सोनिया के जवाब incorrect.This मेरी समाधान, एक छोटा सा मुश्किल है।

package main 

import "fmt" 

func sender(out chan int, exit chan bool){ 
    for i := 1; i <= 10; i++ { 
     out <- i 
    } 
    exit <- true 
} 

func main(){ 
    out := make(chan int, 10) 
    exit := make(chan bool) 

    go sender(out, exit) 

    L: 
    for { 
     select { 
      case i := <-out: 
       fmt.Printf("Value: %d\n", i) 
      case <-exit: 
       for{ 
        select{ 
        case i:=<-out: 
         fmt.Printf("Value: %d\n", i) 
        default: 
         fmt.Println("Exiting") 
         break L 
        } 
       } 
       fmt.Println("Exiting") 
       break L 
     } 
    } 
    fmt.Println("Did we get all 10? Yes!") 
} 
0

क्या बफर्ड चैनल make(chan int, 10) का उपयोग करने के लिए कोई विशिष्ट कारण है?

आपको एक अप्रयुक्त चैनल बनाम buffered का उपयोग करने की आवश्यकता है, जिसका आप उपयोग कर रहे हैं।

बस 10 हटाएं, यह केवल make(chan int) होना चाहिए।

इस तरह sender समारोह में निष्पादन केवल के बाद exit <- true बयान लिए आगे बढ़ सकते out चैनल से पिछले संदेश i := <-out बयान से dequeued है। यदि वह कथन निष्पादित नहीं किया गया है, तो goroutine में exit <- true तक पहुंचने का कोई तरीका नहीं है।

0

यहां एक और विकल्प है।

उपभोक्ता कोड:

go func() { 
    stop := false 
    for { 
     select { 
     case item, _ := <-r.queue: 
     doWork(item) 
     case <-r.stopping: 
     stop = true 
     } 
     if stop && len(r.queue) == 0 { 
     break 
     } 
    } 
    }()