2008-12-17 13 views
10

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

  • स्टार्टअप (बनाता है) ->
  • सर्वर (ग्राहकों के लिए सुनता है, बनाता है) ->
  • क्लाइंट (आदेश के लिए सुनता है और अवधि डेटा भेजता है)

मैं औसत 100 क्लाइंट मान रहा हूं, क्योंकि यह गेम के लिए किसी भी समय अधिकतम था। पूरी चीज के थ्रेडिंग के लिए सही निर्णय क्या होगा? मेरा वर्तमान सेटअप निम्नानुसार है:

  • नए कनेक्शन के लिए नए कनेक्शन की सूची में सर्वर पर 1 थ्रेड, क्लाइंट ऑब्जेक्ट बनाते हैं और फिर से सुनना शुरू करते हैं।
  • क्लाइंट ऑब्जेक्ट में एक धागा है, आने वाले आदेशों को सुनना और आवधिक डेटा भेजना। यह एक गैर-अवरुद्ध सॉकेट का उपयोग करके किया जाता है, इसलिए यह आसानी से जांचता है कि क्या डेटा उपलब्ध है, उसके साथ सौदा करता है और उसके बाद संदेश भेजता है। प्रेषण चक्र शुरू होने से पहले लॉग इन किया जाता है।
  • खेल के लिए एक धागा (अभी के लिए), जैसा कि मुझे लगता है कि पूरे क्लाइंट-सर्वर भाग से अलग होना, वास्तुशिल्प रूप से बोलना।

इसके परिणामस्वरूप कुल 102 धागे होंगे। मैं क्लाइंट 2 धागे देने, एक भेजने के लिए और एक प्राप्त करने पर भी विचार कर रहा हूं। यदि मैं ऐसा करता हूं, तो मैं रिसीवर थ्रेड पर अवरुद्ध I/O का उपयोग कर सकता हूं, जिसका अर्थ है कि थ्रेड औसत स्थिति में अधिकतर निष्क्रिय होगा।

मेरी मुख्य चिंता यह है कि इस कई धागे का उपयोग करके मैं संसाधनों को छेड़छाड़ कर दूंगा। मैं दौड़ की स्थिति या डेडलॉक्स के बारे में चिंतित नहीं हूं, क्योंकि ऐसा कुछ है जिसे मुझे किसी भी तरह से निपटना होगा।

मेरा डिज़ाइन इस तरह से स्थापित किया गया है कि मैं सभी क्लाइंट संचारों के लिए एक थ्रेड का उपयोग कर सकता हूं, भले ही यह 1 या 100 हो। मैंने संचार तर्क को क्लाइंट ऑब्जेक्ट से अलग कर दिया है, इसलिए मैं कर सकता था बहुत सारे कोड को फिर से लिखने के बिना इसे कार्यान्वित करें।

मुख्य प्रश्न यह है: क्या आवेदन में 200 से अधिक धागे का उपयोग करना गलत है? क्या इसमें फायदे हैं? मैं इसे बहु-कोर मशीन पर चलाने के बारे में सोच रहा हूं, क्या यह इस तरह के कई कोरों का बहुत लाभ उठाएगा?

धन्यवाद!


इन सभी धागे में से, उनमें से अधिकतर आमतौर पर अवरुद्ध हो जाएंगे। मुझे कनेक्शन प्रति 5 मिनट से अधिक होने की उम्मीद नहीं है। ग्राहक से आदेश कम से कम आते हैं, मैं औसतन 20 प्रति मिनट कहूंगा।

मैं यहां प्राप्त उत्तरों से जा रहा हूं (संदर्भ स्विचिंग प्रदर्शन हिट था जिसके बारे में मैं सोच रहा था, लेकिन मुझे नहीं पता था कि जब तक आप इसे इंगित नहीं करते, धन्यवाद!) मुझे लगता है कि मैं एक श्रोता, एक रिसीवर, एक प्रेषक, और कुछ विविध सामग्री के साथ दृष्टिकोण के लिए जाऊंगा ;-)

उत्तर

4

मैं .NET में लिखता हूं और मुझे यकीन नहीं है कि जिस तरह से मैं कोड .NET सीमाओं और उनके एपीआई डिज़ाइन के कारण हूं या यदि यह चीजों को करने का एक मानक तरीका है, लेकिन इस तरह मैंने यह किया है अतीत में चीज की तरह:

  • एक कतार वस्तु जिसका उपयोग आने वाले डेटा को संसाधित करने के लिए किया जाएगा। दौड़ की स्थिति से बचने के लिए यह क्यूईंग थ्रेड और वर्कर थ्रेड के बीच लॉक सिंक होना चाहिए।

  • कतार में डेटा प्रोसेसिंग के लिए एक कार्यकर्ता धागा। डेटा कतार को कतारबद्ध करने वाला धागा कतार में वस्तुओं को संसाधित करने के लिए इस धागे को सूचित करने के लिए सेमफोर का उपयोग करता है। यह धागा किसी भी अन्य धागे से पहले खुद को शुरू कर देगा और इसमें एक सतत लूप होता है जो इसे तब तक चला सकता है जब तक कि उसे शट डाउन अनुरोध प्राप्त न हो जाए। लूप में पहला निर्देश प्रसंस्करण रोकने/जारी/समाप्त करने के लिए एक ध्वज है। ध्वज शुरू में रुकने के लिए सेट किया जाएगा ताकि धागा एक निष्क्रिय स्थिति में बैठे (लगातार लूपिंग के बजाय) जबकि कोई प्रसंस्करण नहीं किया जा रहा है। कतार में वस्तुओं को संसाधित करने के दौरान कतार में धागा ध्वज बदल जाएगा। यह धागा लूप के प्रत्येक पुनरावृत्ति पर कतार में एक ही आइटम को संसाधित करेगा। जब कतार खाली होती है तो यह ध्वज को वापस रोकने के लिए सेट कर देगा ताकि लूप के अगले पुनरावृत्ति पर यह क्यूइंग प्रक्रिया को सूचित करे जब तक कि अधिक काम नहीं किया जाता है।

  • एक कनेक्शन श्रोता धागा जो आने वाले कनेक्शन अनुरोध के लिए सुनता है और के लिए रवाना इन गुजरता है ...

  • एक कनेक्शन प्रसंस्करण धागा कि कनेक्शन/सत्र बनाता है। आपके कनेक्शन श्रोता धागे से अलग धागा होने का मतलब है कि कम संसाधनों के कारण आप मिस्ड कनेक्शन अनुरोधों की संभावना को कम कर रहे हैं, जबकि वह थ्रेड प्रसंस्करण अनुरोध कर रहा है।

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

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

  • कुछ सत्र ऑब्जेक्ट जो विधियों के बीच पारित होते हैं ताकि प्रत्येक उपयोगकर्ता का सत्र स्वयं थ्रेडिंग मॉडल में निहित हो।

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

यह टीडीडी (टेस्ट ड्राइव डेवलपमेंट) के साथ भी सहायता करता है जैसे कि प्रत्येक थ्रेड एक ही कार्य को संसाधित कर रहा है और कोड परीक्षणों के लिए बहुत आसान है। सैकड़ों धागे जल्दी से संसाधन आवंटन दुःस्वप्न बन सकते हैं, जबकि एक धागा एक रखरखाव दुःस्वप्न बन जाता है।

एक धागा प्रति तार्किक कार्य को रखने के लिए यह बहुत आसान है, उसी तरह आपके पास एक टीडीडी वातावरण में प्रति कार्य एक विधि होगी और आप तर्कसंगत रूप से अलग कर सकते हैं कि प्रत्येक को क्या करना चाहिए। संभावित समस्याओं को ढूंढना और उन्हें ठीक करना कहीं आसान है।

7

संतुलन बनाए रखने के लिए एक ईवेंट स्ट्रीम/कतार और थ्रेड पूल का उपयोग करें; इस अन्य मशीनों को कमोबेश कोर

सामान्य रूप में

हो सकता है करने के लिए बेहतर अनुकूलित किया जाएगा, कई और अधिक सक्रिय धागे से आप कोर समय बर्बाद करेंगे संदर्भ-स्विचिंग

अपने खेल लघु कार्यों का एक बहुत कुछ के होते हैं, एक गोलाकार/रीसाइक्लिंग इवेंट कतार एक निश्चित संख्या में थ्रेड

0

मुझे लगता है कि आपको जो प्रश्न पूछना चाहिए, वह यह नहीं है कि 200 सामान्य थ्रेड नंबर के रूप में अच्छा या बुरा है, बल्कि यह कितने धागे हैं सक्रिय होने जा रहा है।

यदि उनमें से केवल कुछ ही किसी भी पल में सक्रिय हैं, जबकि अन्य सभी सो रहे हैं या प्रतीक्षा कर रहे हैं या नहीं, तो आप ठीक हैं। सोते धागे, इस संदर्भ में, आपको कुछ भी लागत नहीं है।

हालांकि अगर उन सभी 200 धागे सक्रिय हैं, तो आप अपने CPU को उन सभी ~ 200 धागे के बीच थ्रेड संदर्भ स्विच करने में इतना समय बर्बाद कर रहे हैं।

+0

बकवास, एक नींद धागा में 1 एमबी स्टैक है, इसलिए 200 सोते धागे 200 एमबी बर्बाद स्मृति हैं। –

+0

किसी गेम में 100 क्लाइंट प्रबंधित करने में सक्षम सर्वर में, शोर में 200 एमबी अपशिष्ट स्थान लगभग नीचे है। –

+0

@ एरविकर - उद्धरण कृपया? –

2

आपका मंच क्या है? यदि विंडोज़ तो मैं सुझाव देता हूं कि एसिंक ऑपरेशंस और थ्रेड पूल (या I/O प्राप्ति बंदरगाहों को सीधे देखें यदि आप सी/सी ++ में Win32 API स्तर पर काम कर रहे हैं)।

विचार यह है कि आपके पास अपने I/O से निपटने वाले धागे की एक छोटी संख्या है और यह आपके सिस्टम को बड़ी संख्या में समवर्ती कनेक्शनों को स्केल करने में सक्षम बनाता है क्योंकि कनेक्शन की संख्या और धागे की संख्या के बीच कोई संबंध नहीं है उन प्रक्रियाओं द्वारा उपयोग किया जाता है जो उनकी सेवा कर रहे हैं। जैसा कि अपेक्षित है, नेट आपको विवरण से insulates और Win32 नहीं करता है।

एसिंक I/O का उपयोग करने की चुनौती और सर्वर की यह शैली यह है कि क्लाइंट अनुरोधों की प्रक्रिया सर्वर पर एक राज्य मशीन बन जाती है और डेटा आने वाले डेटा को राज्य के परिवर्तनों को ट्रिगर करता है। कभी-कभी यह कुछ करने के लिए उपयोग किया जाता है लेकिन एक बार जब आप इसे वास्तव में अद्भुत बनाते हैं;)

मुझे कुछ मुफ्त कोड मिला है जो आईओसीपी here का उपयोग कर सी ++ में विभिन्न सर्वर डिज़ाइन प्रदर्शित करता है।

यदि आप यूनिक्स का उपयोग कर रहे हैं या क्रॉस प्लेटफार्म होने की आवश्यकता है और आप सी ++ में हैं तो आप एएसआईओ को बढ़ावा देना चाहेंगे जो एसिंक I/O कार्यक्षमता प्रदान करता है।

5

प्रश्न का उत्तर देने के लिए, आज के हार्डवेयर पर 200 धागे का उपयोग करना पूरी तरह गलत है।

प्रत्येक थ्रेड मेमोरी 1 एमबी लेता है, इसलिए आप कुछ भी उपयोगी करने से पहले 200 एमबी पेज फ़ाइल ले रहे हैं।

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

अद्यतन: 200 एमबी पदार्थ बर्बाद कर रहा है? 32-बिट मशीन पर, यह प्रक्रिया के लिए संपूर्ण सैद्धांतिक पता स्थान का 10% है - कोई और प्रश्न नहीं। 64-बिट मशीन पर, यह लगता है सैद्धांतिक रूप से उपलब्ध होने के सागर में एक बूंद की तरह लगता है, लेकिन व्यावहारिक रूप से यह अभी भी एक बहुत बड़ा हिस्सा है (या बल्कि, बड़ी संख्या में बड़े बड़े हिस्से) भंडारण के निर्विवाद रूप से आरक्षित है आवेदन द्वारा, और फिर ओएस द्वारा प्रबंधित किया जाना है।इसका प्रत्येक ग्राहक की बहुमूल्य जानकारी के आस-पास के प्रभाव का असर पड़ता है, जिसमें बहुत सारे बेकार पैडिंग होते हैं, जो स्थानीयता को नष्ट कर देते हैं, ओएस को हराते हैं और सीपीयू के कैश की सबसे तेज परतों में अक्सर एक्सेस की गई सामग्री को रखने के प्रयासों को हराते हैं।

किसी भी मामले में, स्मृति बर्बादी पागलपन का केवल एक हिस्सा है। जब तक आपके पास 200 कोर (और एक ओएस उपयोग करने में सक्षम) नहीं है तो आपके पास वास्तव में 200 समांतर धागे नहीं हैं। आपके पास (8) 8 कोर हैं, प्रत्येक मोटे तौर पर 25 धागे के बीच स्विचिंग करते हैं। नैतिक रूप से आप सोच सकते हैं कि इसके परिणामस्वरूप, प्रत्येक थ्रेड को कोर पर चलने के बराबर अनुभव होता है जो 25 गुना धीमा होता है। लेकिन यह वास्तव में उससे भी बदतर है - ओएस कोर को एक धागा से अधिक थ्रेड लेता है और उस पर एक और डालता है ("संदर्भ स्विचिंग") इससे वास्तव में आपके कोड को चलाने की इजाजत मिलती है।

बस देखें कि कोई भी प्रसिद्ध सफल डिज़ाइन इस तरह की समस्या का सामना कैसे करता है। सीएलआर का थ्रेड पूल (भले ही आप इसका उपयोग नहीं कर रहे हों) एक अच्छा उदाहरण के रूप में कार्य करता है। यह मानना ​​शुरू होता है कि प्रति कोर केवल एक थ्रेड पर्याप्त होगा। यह और अधिक बनाने की अनुमति देता है, लेकिन केवल यह सुनिश्चित करने के लिए कि बुरी तरह से डिजाइन किए गए समांतर एल्गोरिदम अंततः पूर्ण हो जाएंगे। यह प्रति सेकंड 2 से अधिक धागे बनाने से इंकार कर देता है, इसलिए यह उन्हें धीमा करके थ्रेड-लालची एल्गोरिदम को प्रभावी ढंग से दंडित करता है।

+0

क्या 200 एमबी आजकल महत्वपूर्ण है? संभवतः यह कम या ज्यादा समर्पित सर्वर पर होगा - आप बिना किसी ध्यान के अपने वर्कस्टेशन पर 100 गेम प्लेयर चलाने की उम्मीद नहीं करते हैं। –

+0

yup, 200MB बहुत है, खासकर यदि आप अन्य चीजों को चलाने की कोशिश कर रहे हैं। इसके अलावा, शेष ऐप को स्मृति की आवश्यकता होगी ताकि कम से कम 200 एमबी बर्बाद 200 एमबी कम कैश हो। – gbjbaanb

+0

धन्यवाद। मुझे लगता है कि मैं कह सकता हूं कि पहली बार मुझे ऐसी स्थिति का सामना करना पड़ा जहां मुझे वास्तव में ऐसे संसाधनों के बारे में सोचना पड़ा। मुझे लगता है कि मैंने एक अच्छी शिक्षा परियोजना चुनी है, क्योंकि मैं अपने कड़ी मेहनत से परिणाम देखना पसंद करूंगा ;-) –

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