2012-04-10 11 views
38

मैं गो में सहमति को समझने की कोशिश कर रहा हूं।गोरोटाइन्स को समझना

package main 

import "fmt" 

var x = 1 

func inc_x() { //test 
    for { 
    x += 1 
    } 
} 

func main() { 
    go inc_x() 
    for { 
    fmt.Println(x) 
    } 
} 

मैं स्वीकार करते हैं कि मैं x साथ दौड़ की स्थिति को रोकने के लिए चैनल का उपयोग किया जाना चाहिए, लेकिन उस समय यहाँ नहीं है: विशेष रूप से, मैं इस धागे की असुरक्षित कार्यक्रम लिखा था। कार्यक्रम 1 प्रिंट करता है और फिर हमेशा लूप लगता है (कुछ भी प्रिंट किए बिना)। मैं उम्मीद करता हूं कि यह संख्याओं की एक अनंत सूची मुद्रित करे, संभवतः कुछ को छोड़कर और दौड़ की स्थिति के कारण दूसरों को दोहराएं (या बदतर - inc_x में अपडेट होने पर संख्या को प्रिंट करना)।

मेरा प्रश्न है: प्रोग्राम केवल एक पंक्ति क्यों प्रिंट करता है?

बस स्पष्ट होना: मैं इस खिलौने उदाहरण के लिए उद्देश्य पर चैनलों का उपयोग नहीं कर रहा हूँ।

उत्तर

32

कुछ चीजें जाओ के goroutines के बारे में ध्यान में रखना है कर रहे हैं के रूप में।

  1. वे जावा या सी ++ धागे के अर्थ में धागे नहीं हैं।
    1. वे greenlets की तरह अधिक कर रहे हैं। प्रणाली धागे
      1. प्रणाली धागे की संख्या 1 वर्तमान में मुझे लगता है कि एक वातावरण चर GOMAXPROCS और चूक द्वारा नियंत्रित किया जाता भर में
    2. जाने क्रम मल्टीप्लेक्स goroutines। यह भविष्य में बदल सकता है।
  2. रास्ता goroutines अपने मौजूदा धागा करने के लिए वापस उपज कई अलग अलग निर्माणों द्वारा नियंत्रित है।
    1. चयन कथन नियंत्रण को धागे पर वापस ला सकता है।
    2. किसी चैनल पर भेजने से थ्रेड पर नियंत्रण मिल सकता है।
    3. आईओ संचालन करना धागे पर नियंत्रण वापस ला सकता है।
    4. रनटाइम। गोस्ड() स्पष्ट रूप से धागे पर नियंत्रण उत्पन्न करता है।

व्यवहार आप देख रहे हैं मुख्य कार्य कभी नहीं धागा करने के लिए वापस पैदावार और इसके बजाय एक व्यस्त पाश में शामिल है और वहाँ के बाद से केवल एक धागा मुख्य लूप को चलाने के लिए कोई जगह नहीं है क्योंकि है।

+0

बस सोचा कि मैं उल्लेख करता हूं कि गो 1.2 के बाद, समय-समय पर फ़ंक्शन एंट्री पर शेड्यूलर लगाया जा सकता है। यह इस मामले को हल करेगा जो मुझे नहीं लगता है, लेकिन यह तब मदद करता है जब आपके पास एक गैर-इनलाइन फ़ंक्शन को कॉल करने वाला तंग लूप होता है। –

2

कोई निश्चित नहीं है, लेकिन मुझे लगता है कि inc_x सीपीयू को हॉगिंग कर रहा है। चूंकि कोई आईओ नहीं है क्योंकि यह नियंत्रण जारी नहीं करता है।

मुझे दो चीजें मिलीं जो हल हुईं। कार्यक्रम की शुरुआत में runtime.GOMAXPROCS(2) पर कॉल करना था और फिर यह काम करेगा क्योंकि अब दो धागे गोरउटिंग की सेवा कर रहे हैं। दूसरा x बढ़ाने के बाद time.Sleep(1) डालना है।

17

this और this के अनुसार, कुछ कॉल सीपीयू-बाउंड गोरौटाइन (यदि गोरौटाइन शेड्यूलर तक कभी नहीं आती) के दौरान नहीं आ सकती है। यह अगर वे मुख्य थ्रेड ब्लॉक करने के लिए

समाधान मैं शामिल करने के लिए वापस उपज के लिए अपने CPU बाध्य सूत्र में runtime.Gosched() बुला पाया (जैसे fmt.Println() द्वारा प्रयोग किया जाता write() syscall के साथ मामला है) की जरूरत है अन्य Goroutines लटका पैदा कर सकता है अनुसूचक, के रूप में इस प्रकार है:

package main 

import (
    "fmt" 
    "runtime" 
) 

var x = 1 

func inc_x() { 
    for { 
    x += 1 
    runtime.Gosched() 
    } 
} 

func main() { 
    go inc_x() 
    for { 
    fmt.Println(x) 
    } 
} 

क्योंकि आप केवल Goroutine में एक आपरेशन प्रदर्शन कर रहे हैं, runtime.Gosched()बहुत अक्सर कहा जाता है की जा रही है। Init पर runtime.GOMAXPROCS(2) कॉलिंग परिमाण के क्रम से तेज़ है, लेकिन यदि आप किसी संख्या को बढ़ाने से अधिक जटिल कर रहे थे (उदाहरण के लिए, सरणी, structs, मानचित्र, आदि से निपटने) की तुलना में कुछ अधिक जटिल हो रहा था, तो बहुत थ्रेड-असुरक्षित होगा।

उस मामले में, सबसे अच्छा अभ्यास संभवतः एक संसाधन के लिए साझा पहुँच प्रबंधन के लिए एक चैनल का उपयोग किया जाएगा।

अद्यतन: जाओ 1.2, any non-inlined function call can invoke the scheduler.

+3

क्या यह कहना सही है कि यह समस्या चैनलों के उचित उपयोग के साथ कभी नहीं होती है? अन्यथा यह गो के साथ एक बड़ी समस्या की तरह लगता है - अगर एक धागा सीपीयू को हॉग कर सकता है और कोई अन्य थ्रेड निष्पादित नहीं होता है। –

+0

यदि आपके पास एकाधिक सीपीयू हैं तो आप पर्यावरण चर को GOMAXPROCS = 2 पर भी सेट कर सकते हैं और फिर goroutine मुख्य फ़ंक्शन की तुलना में अलग थ्रेड में चला सकता है। गोस्ड() फ़ंक्शन रनटाइम को लूप के लिए उसमें उपज करने के लिए कहता है। –

+0

@ user793587 "उचित" से आपका क्या मतलब है इस पर निर्भर करता है। एक तंग पाश में मतदान एक धागा हॉग कर सकते हैं, लेकिन कोड खराब है वैसे भी। व्यवहार में, यह सिर्फ एक समस्या नहीं है। दुर्लभ घटना में आपको * तंग लूप में मतदान करने की आवश्यकता है, आप स्पष्ट रूप से शेड्यूलर को उपज कर सकते हैं, लेकिन यह आमतौर पर खिलौनों के उदाहरणों में आता है। मैंने प्रीपेप्टिव शेड्यूलर पर स्विच करने की योजनाओं के बारे में सुना है, लेकिन वर्तमान सहकारी शेड्यूलर ज्यादातर मामलों में अच्छी तरह से काम करता है। – SteveMcQwark

7

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

"कोर" कहना एक चमक का थोड़ा सा है। जाओ वास्तव में दृश्यों के पीछे कई कोर का उपयोग कर सकते हैं, लेकिन यह गैर-सिस्टम कार्यों को निष्पादित करने वाले आपके goroutines शेड्यूल करने के लिए थ्रेड की संख्या निर्धारित करने के लिए GOMAXPROCS नामक चर का उपयोग करता है। जैसा कि FAQ और Effective Go में बताया गया है कि डिफ़ॉल्ट 1 है, लेकिन इसे पर्यावरण चर या रनटाइम फ़ंक्शन के साथ उच्च सेट किया जा सकता है। यह संभवतः आपके द्वारा अपेक्षित आउटपुट देगा, लेकिन केवल तभी होगा जब आपके प्रोसेसर में एकाधिक कोर हों।

स्वतंत्र रूप से कोर और GOMAXPROCS के, आप रनटाइम में गोरौटाइन शेड्यूलर को अपना काम करने का मौका दे सकते हैं। शेड्यूलर एक चल रहे goroutine को पूर्ववत नहीं कर सकता है, लेकिन इसे रनटाइम पर वापस आने के लिए इंतजार करना चाहिए और कुछ सेवा, जैसे IO, time.Sleep(), या runtime.Gosched() का अनुरोध करना चाहिए। Inc_x में इस तरह कुछ जोड़ना अपेक्षित आउटपुट उत्पन्न करता है। Goroutine चल रहा मुख्य() पहले से ही fmt.Println के साथ एक सेवा का अनुरोध कर रहा है, इसलिए समय-समय पर चलने वाले दो goroutines के साथ, यह कुछ प्रकार के उचित शेड्यूलिंग कर सकता है।

+0

आप एक प्रोसेसर पर एकाधिक एक साथ थ्रेड/प्रक्रियाएं चला सकते हैं, इसलिए "केवल अगर आपके प्रोसेसर में एकाधिक कोर हैं" भ्रामक है। – lunixbochs

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