मैं एक बहुआयामी इंजन कम बोझिल में धागे के बीच गुजरने वाला संदेश कैसे बना सकता हूं?


18

वर्तमान में मैं जिस C ++ इंजन पर काम कर रहा हूं, वह कई बड़े थ्रेड- जेनरेशन (मेरी प्रक्रियात्मक सामग्री बनाने के लिए), गेमप्ले (AI, स्क्रिप्ट, सिमुलेशन के लिए), भौतिकी और प्रतिपादन में विभाजित है।

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

इस प्रक्रिया की शुरुआत में और मैंने कुछ चीजों पर ध्यान दिया है:

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

    क्या ऐसा करने के लिए इससे अच्छा तरीका है? क्या मुझे बढ़ावा देने के लिए कुछ का उपयोग करना चाहिए :: इसे स्वचालित बनाने के लिए बाध्य करें? मुझे चिंता है कि अगर मैं ऐसा करता हूं कि मैं कहने की क्षमता खो देता हूं, तो संदेश को प्रकार, या कुछ के आधार पर क्रमबद्ध करता हूं। निश्चित नहीं है कि अगर इस तरह का प्रबंधन आवश्यक हो जाएगा।

  2. पहला बिंदु महत्वपूर्ण है क्योंकि ये धागे बहुत संवाद करते हैं। संदेश बनाना और पास करना चीजों को बनाने का एक बड़ा हिस्सा है। मैं उस प्रणाली को सुव्यवस्थित करना चाहता हूं, लेकिन अन्य प्रतिमानों के लिए भी खुला होना चाहिए जो सहायक हो सकते हैं। वहाँ विभिन्न multithreaded डिजाइन मैं इस आसान बनाने में मदद करने के बारे में सोचना चाहिए?

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

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


6
यहाँ वास्तव में एकल, निर्देशित प्रश्न नहीं है, और इस तरह इस पोस्ट के लिए इस साइट की क्यू एंड ए शैली के लिए एक अच्छा फिट नहीं है। मैं आपको अपनी पोस्ट को अलग-अलग पोस्टों में तोड़ने की सलाह दूंगा, एक प्रश्न के अनुसार, और प्रश्नों को फिर से भरना ताकि वे एक विशिष्ट समस्या के बारे में पूछ रहे हों जो वास्तव में आपके सुझावों या सलाह के अस्पष्ट संग्रह के बजाय हो रही हैं।

2
यदि आप अधिक सामान्य बातचीत में शामिल होना चाहते हैं, तो मैं आपको इस पोस्ट को gamedev.net पर मंचों पर आज़माने की सलाह दूंगा । जैसा कि जोश ने कहा, चूँकि आपका "प्रश्न" एक विशिष्ट प्रश्न नहीं है, इसलिए StackExchange प्रारूप में इसे व्यवस्थित करना काफी कठिन होगा।
साइपर

राय देने के लिए आप सभी का धन्यवाद! मैं उम्मीद कर रहा था कि किसी के पास अधिक ज्ञान के साथ एक ही संसाधन / अनुभव / प्रतिमान हो सकता है जो एक बार में मेरे कई मुद्दों को संबोधित कर सकता है। मुझे लग रहा है कि एक बड़ा विचार मेरी विभिन्न समस्याओं को एक चीज़ में बदल सकता है, जो मुझे याद आ रही है, और मैं किसी को अनुभव से अधिक सोच रहा था, जो शायद मैं पहचान सकता हूँ ... लेकिन शायद नहीं, और किसी भी तरह से - अंक लिया !
Raptormeat

मैंने आपका शीर्षक बदलकर संदेश पारित करने के लिए और अधिक विशिष्ट होने का नाम दिया, क्योंकि "टिप्स" प्रकार के प्रश्न का अर्थ है कि हल करने के लिए कोई विशेष समस्या नहीं है (और इसलिए इन दिनों मैं "नहीं एक वास्तविक प्रश्न के रूप में बंद कर दूंगा")।
तेतराद

क्या आप सुनिश्चित हैं कि आपको भौतिकी और गेमप्ले के लिए अलग थ्रेड की आवश्यकता है? उन दोनों को बहुत इंटरवेट किया गया लगता है। इसके अलावा, यह जानना मुश्किल है कि बिना यह सलाह दिए कि उनमें से प्रत्येक कैसे और किसके साथ संवाद करता है।
निकोल बोलस Nic

जवाबों:


10

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

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


1
चेक आउट करने के लिए विशिष्ट थ्रेड पूलिंग के लिए कोई सिफारिशें?
इमरे

निक- प्रतिक्रिया के लिए बहुत बहुत धन्यवाद। अपने पहले बिंदु के रूप में- मुझे लगता है कि यह एक महान विचार है और शायद मैं जिस दिशा में आगे बढ़ूंगा। फिलहाल यह काफी जल्दी है कि मुझे अभी तक नहीं पता है कि डबल-बफ़र्ड होने की क्या आवश्यकता होगी। मैं इसे ध्यान में रखूंगा क्योंकि यह समय के साथ जमता है। आपके दूसरे बिंदु पर- सुझाव के लिए धन्यवाद! हां, थ्रेड कार्यों के लाभ स्पष्ट हैं। मैं आपके लिंक पढ़ूंगा और इसके बारे में सोचूंगा। 100% निश्चित नहीं है कि यह मेरे लिए काम करेगा / यह मेरे लिए कैसे काम करेगा लेकिन मैं इसे निश्चित रूप से गंभीर विचार दूंगा। धन्यवाद!
Raptormeat

1
@imre Boost लाइब्रेरी की जाँच करें - उनके पास वायदा है, जो इन चीजों के करीब पहुंचने का एक अच्छा / आसान तरीका है।
जोनाथन डिकिंसन 21

5

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

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


4

हमारे पास एक ही समस्या है, केवल C # के साथ। नए संदेशों को बनाने में आसानी (या इसके अभाव) के बारे में लंबे समय तक सोचने के बाद, सबसे अच्छा हम उनके लिए एक कोड-जनरेटर बनाने के लिए कर सकते थे। यह थोड़ा बदसूरत है, लेकिन प्रयोग करने योग्य है: केवल संदेश सामग्री का विवरण दिया गया है, यह संदेश वर्ग, एनम, प्लेसहोल्डर हैंडलिंग कोड आदि उत्पन्न करता है - यह सब कोड जो लगभग हर बार एक ही है, और वास्तव में टाइपो-प्रोन है।

मैं इससे पूरी तरह से खुश नहीं हूं, लेकिन यह सब उस कोड को हाथ से लिखने से बेहतर है।

साझा किए गए डेटा के बारे में, सबसे अच्छा जवाब निश्चित रूप से "यह निर्भर करता है" है। लेकिन आम तौर पर, अगर कुछ डेटा को अक्सर पढ़ा जाता है, और कई थ्रेड्स की आवश्यकता होती है, तो इसे साझा करना इसके लायक है। थ्रेड सेफ्टी के लिए, आपका सबसे अच्छा दांव इसे अपरिवर्तनीय बना रहा है , लेकिन यदि यह प्रश्न से बाहर है, तो म्यूटेक्स कर सकता है। सी # में एक ReaderWriterLockSlimवर्ग है, विशेष रूप से ऐसे मामलों के लिए डिज़ाइन किया गया है; मुझे यकीन है कि C ++ समकक्ष है।

थ्रेड कम्युनिकेशन के लिए एक अन्य विचार, जो संभवतः आपकी पहली समस्या को हल करता है, संदेश के बजाय हैंडलर पास करना है। मुझे यकीन नहीं है कि इसे C ++ में कैसे काम करना है, लेकिन C # में आप delegateकिसी अन्य थ्रेड में ऑब्जेक्ट भेज सकते हैं (जैसा कि, इसे किसी प्रकार की संदेश कतार में जोड़ें), और वास्तव में प्राप्त थ्रेड से इस प्रतिनिधि को कॉल करें। इससे मौके पर "तदर्थ" संदेश बनाना संभव हो जाता है। मैंने केवल इस विचार के साथ खिलवाड़ किया, वास्तव में उत्पादन में कभी इसकी कोशिश नहीं की, इसलिए यह वास्तव में खराब हो सकता है।


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

1

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

  • अधिकांश गेम डेटा को केवल-पढ़ने के लिए साझा किया जाना चाहिए ।
  • एक प्रकार के संदेश का उपयोग करके डेटा लिखना संभव है।
  • डेटा को अपडेट करने से बचने के लिए, जबकि दूसरा धागा इसे पढ़ रहा है, गेम लूप के दो अलग-अलग चरण हैं: पढ़ें और अपडेट करें।
  • पढ़ने के चरण में:
  • सभी साझा डेटा केवल सभी थ्रेड्स के लिए पढ़े जाते हैं।
  • थ्रेड्स सामान की गणना कर सकते हैं (थ्रेड-लोकल स्टोरेज का उपयोग करके), और अपडेट अनुरोधों का उत्पादन करते हैं , जो मूल रूप से कमांड / संदेश ऑब्जेक्ट होते हैं, जिन्हें कतार में रखा जाता है, बाद में लागू किया जाता है।
  • अद्यतन चरण में:
  • सभी साझा डेटा केवल-लेखन है। डेटा को अज्ञात / अस्थिर अवस्था में ग्रहण किया जाना है।
  • यह वह जगह है जहाँ अद्यतन अनुरोध ऑब्जेक्ट संसाधित होते हैं।

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

कुल मिलाकर, मुझे लगता है कि यह दृष्टिकोण एक थ्रेड पूलिंग सिस्टम के लिए अच्छा होगा। समस्याग्रस्त भाग हैं:

  • अपडेट थ्रेड्स को सिंक करना (सुनिश्चित करें कि एक ही डेटा सेट को अपडेट करने के लिए कोई एकाधिक थ्रेड्स प्रयास न करें)।
  • यह सुनिश्चित करते हुए कि रीड चरण में कोई भी धागा गलती से साझा डेटा नहीं लिख सकता है। मुझे डर है कि प्रोग्रामिंग गलतियों के लिए बहुत अधिक जगह होगी, और मुझे यकीन नहीं है कि उनमें से कितने को आसानी से डिबग टूल द्वारा पकड़ा जा सकता है।
  • इस तरह से कोड लिखना जैसे कि आप अपने मध्यवर्ती परिणामों पर तुरंत पढ़ने के लिए उपलब्ध होने पर भरोसा नहीं कर सकते। x += 2; if (x > 5) ...यदि x साझा किया गया है, तो आप लिख नहीं सकते । आपको या तो x की एक स्थानीय प्रतिलिपि बनाने की ज़रूरत है, या एक अद्यतन अनुरोध का उत्पादन करना है, और केवल अगले रन में सशर्त करना है। उत्तरार्द्ध का अर्थ होता है बॉयलर-कोड को संरक्षित करने वाले अतिरिक्त थ्रेड-लोकल स्टेट की पूरी श्रृंखला।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.