2012-09-27 15 views
6

मैं जाओ टूर exercise #71golang: जब तक मैं एक fmt.Print()

की कोशिश की, यह ठीक काम करता है go run 71_hang.go ok की तरह चलाया जाता है तो जोड़ा चयन के साथ goroute नहीं रूकती है।

हालांकि, यदि आप go run 71_hang.go nogood का उपयोग करते हैं, तो यह हमेशा के लिए चलाएगा।

select कथन में default में अतिरिक्त fmt.Print("") अतिरिक्त अंतर है।

मुझे यकीन नहीं है, लेकिन मुझे किसी प्रकार की अनंत लूप और रेस-हालत पर संदेह है? और यहाँ मेरा समाधान है।

ध्यान दें: यह गतिरोध नहीं कर रहा है के रूप में जाओ throw: all goroutines are asleep - deadlock!

package main 

import (
    "fmt" 
    "os" 
) 

type Fetcher interface { 
    // Fetch returns the body of URL and 
    // a slice of URLs found on that page. 
    Fetch(url string) (body string, urls []string, err error) 
} 

func crawl(todo Todo, fetcher Fetcher, 
    todoList chan Todo, done chan bool) { 
    body, urls, err := fetcher.Fetch(todo.url) 
    if err != nil { 
     fmt.Println(err) 
    } else { 
     fmt.Printf("found: %s %q\n", todo.url, body) 
     for _, u := range urls { 
      todoList <- Todo{u, todo.depth - 1} 
     } 
    } 
    done <- true 
    return 
} 

type Todo struct { 
    url string 
    depth int 
} 

// Crawl uses fetcher to recursively crawl 
// pages starting with url, to a maximum of depth. 
func Crawl(url string, depth int, fetcher Fetcher) { 
    visited := make(map[string]bool) 
    doneCrawling := make(chan bool, 100) 
    toDoList := make(chan Todo, 100) 
    toDoList <- Todo{url, depth} 

    crawling := 0 
    for { 
     select { 
     case todo := <-toDoList: 
      if todo.depth > 0 && !visited[todo.url] { 
       crawling++ 
       visited[todo.url] = true 
       go crawl(todo, fetcher, toDoList, doneCrawling) 
      } 
     case <-doneCrawling: 
      crawling-- 
     default: 
      if os.Args[1]=="ok" { // * 
       fmt.Print("") 
      } 
      if crawling == 0 { 
       goto END 
      } 
     } 
    } 
END: 
    return 
} 

func main() { 
    Crawl("http://golang.org/", 4, fetcher) 
} 

// fakeFetcher is Fetcher that returns canned results. 
type fakeFetcher map[string]*fakeResult 

type fakeResult struct { 
    body string 
    urls []string 
} 

func (f *fakeFetcher) Fetch(url string) (string, []string, error) { 
    if res, ok := (*f)[url]; ok { 
     return res.body, res.urls, nil 
    } 
    return "", nil, fmt.Errorf("not found: %s", url) 
} 

// fetcher is a populated fakeFetcher. 
var fetcher = &fakeFetcher{ 
    "http://golang.org/": &fakeResult{ 
     "The Go Programming Language", 
     []string{ 
      "http://golang.org/pkg/", 
      "http://golang.org/cmd/", 
     }, 
    }, 
    "http://golang.org/pkg/": &fakeResult{ 
     "Packages", 
     []string{ 
      "http://golang.org/", 
      "http://golang.org/cmd/", 
      "http://golang.org/pkg/fmt/", 
      "http://golang.org/pkg/os/", 
     }, 
    }, 
    "http://golang.org/pkg/fmt/": &fakeResult{ 
     "Package fmt", 
     []string{ 
      "http://golang.org/", 
      "http://golang.org/pkg/", 
     }, 
    }, 
    "http://golang.org/pkg/os/": &fakeResult{ 
     "Package os", 
     []string{ 
      "http://golang.org/", 
      "http://golang.org/pkg/", 
     }, 
    }, 
} 

उत्तर

15

अपने select बदल जाता है जिस तरह से चयन कार्य करने का एक default बयान लाना नहीं किया। डिफ़ॉल्ट विवरण के बिना चयन चैनलों पर किसी भी संदेश के लिए इंतजार कर देगा। एक डिफ़ॉल्ट कथन चयन के साथ चैनल से पढ़ने के लिए हर बार डिफ़ॉल्ट कथन चलाएगा। आपके कोड में मुझे लगता है कि यह एक अनंत लूप बनाता है। fmt.Print कथन डालने से शेड्यूलर अन्य goroutines शेड्यूल करने की अनुमति दे रहा है।

यदि आप इस तरह अपना कोड बदलते हैं तो यह एक गैर अवरोधक तरीके से चयन का उपयोग करके ठीक से काम करता है जो अन्य goroutines ठीक से चलाने की अनुमति देता है।

for { 
     select { 
     case todo := <-toDoList: 
      if todo.depth > 0 && !visited[todo.url] { 
       crawling++ 
       visited[todo.url] = true 
       go crawl(todo, fetcher, toDoList, doneCrawling) 
      } 
     case <-doneCrawling: 
      crawling-- 
     } 
     if crawling == 0 { 
      break 
     } 
    } 

यदि आप GOMAXPROCS = 2 का उपयोग जो एक और संकेत है कि अनुसूचक अनंत लूप में व्यस्त है है अपने मूल कोड काम कर सकते हैं।

ध्यान दें कि goroutines सह-संचालन निर्धारित हैं। जो मैं आपकी समस्या के बारे में पूरी तरह से समझ नहीं पा रहा हूं वह यह है कि select एक बिंदु है जहां गोरौटाइन पैदा करना चाहिए - मुझे उम्मीद है कि कोई और यह समझा सकता है कि यह आपके उदाहरण में क्यों नहीं है।

+1

चयन उपज नहीं है * क्योंकि डिफ़ॉल्ट बयान के * ।हालांकि मुझे यकीन नहीं है कि अगर आप पूरी तरह से समझ नहीं पाते हैं, क्योंकि आपने "डिफ़ॉल्ट" और GOMAXPROCS स्पष्टीकरणों को खींचा है। – mna

+1

यही वही है जो मुझे नहीं पता था, धन्यवाद! –

+0

"डिफ़ॉल्ट कथन के कारण चयन नहीं करता है।" वह है जो मुझे नहीं पता। धन्यवाद। – Sungam

5

आपके पास 100% सीपीयू लोड है क्योंकि लगभग हर बार डिफ़ॉल्ट केस निष्पादित किया जाएगा, जिसके परिणामस्वरूप प्रभावी रूप से अनंत लूप में परिणाम होता है क्योंकि इसे बार-बार निष्पादित किया जाता है। इस स्थिति में गो शेड्यूलर डिजाइन द्वारा किसी अन्य goroutine पर नियंत्रण नहीं रखता है। तो किसी भी अन्य goroutine को crawling != 0 सेट करने का अवसर कभी नहीं होगा और आपके पास अनंत अनंत लूप होगा।

मेरी राय में आपको डिफ़ॉल्ट केस को हटा देना चाहिए और इसके बजाय यदि आप चयन कथन के साथ खेलना चाहते हैं तो एक और चैनल बनाएं।

अन्यथा runtime पैकेज आप गंदा रास्ता तय करने में मदद करता:,

  • runtime.GOMAXPROCS(2) काम करेंगे (या निर्यात GOMAXPROCS = 2) इस तरह से आप निष्पादन के एक से अधिक ओएस धागा होगा
  • कॉल runtime.Gosched() समय-समय पर क्रॉल के अंदर। घटनाक्रम सीपीयू लोड 100% है, यह स्पष्ट रूप से अन्य गोरौटाइन पर नियंत्रण पारित करेगा।

संपादित करें: हाँ, और कारण है कि fmt.Printf कुछ फ़र्क पड़ता है: क्योंकि यह स्पष्ट रूप से कुछ syscall सामान को नियंत्रण गुजरता है ...;)

+0

"इस स्थिति में गो शेड्यूलर डिजाइन द्वारा किसी अन्य goroutine पर नियंत्रण नहीं रखता है।" पूरी तरह से सच नहीं है। जाओ 1.0 (?) शेड्यूलर यह करता है, लेकिन यह एक अपूर्ण शेड्यूलर है। आपके द्वारा सूचीबद्ध वर्कअराउंड (या 'fmt.Println() 'को कॉल करके एक सिस्कल करना) शेड्यूलर को जगाएगा। जाओ 1.2 में इन सुधारों के विवरण के लिए http://golang.org/doc/go1.2#preemption देखें। – ayke

संबंधित मुद्दे