2010-08-13 4 views
13

में एक विधि को सीमित करने का समय मेरे पास एक गेम फ्रेमवर्क है जहां आईबॉटइंटरफेस को लागू करने वाले बॉट्स की एक सूची है। ये बॉट उपयोगकर्ता द्वारा बनाई गई एकमात्र सीमा के साथ कस्टम हैं जिन्हें उन्हें इंटरफ़ेस को लागू करना होगा।सी #

गेम फिर आपके टर्न और राउंड स्टार्ट जैसी विभिन्न घटनाओं के लिए बॉट्स (उम्मीदवारों में समानांतर) में विधियों को कॉल करता है। मैं चाहता हूं कि बॉट कंप्यूटिंग छोड़ने के लिए मजबूर होने से पहले इन घटनाओं को संभालने में सीमित समय बिताएं।

बात मैं कोशिश कर रहा हूँ की तरह का एक उदाहरण है: (जहां newgame एक प्रतिनिधि है)

Parallel.ForEach(Bots, delegate(IBot bot) 
       { 
        NewGame del = bot.NewGame; 
        IAsyncResult r = del.BeginInvoke(Info, null, null); 
        WaitHandle h = r.AsyncWaitHandle; 
        h.WaitOne(RoundLimit); 
        if (!r.IsCompleted) 
        { 
         del.EndInvoke(r); 
        } 
       } 
      ); 

इस मामले में मैं EndInvoke() जो समाप्त नहीं हो सकता है चलाने के लिए मजबूर कर रहा हूँ। मैं थ्रेड को साफ करने के तरीके के बारे में सोच नहीं सकता।

यह अगर वहाँ

try { 
bot.NewGame(Info); 
} catch (TimeOutException) { 
// Tell bot off. 
} finally { 
// Compute things. 
} 

किसी तरह का था बहुत अच्छा होगा लेकिन मुझे लगता है इसके संभावित इस तरह के एक निर्माण करने के लिए नहीं है।

इसका उद्देश्य शान से ऐ के जो आकस्मिक अनंत लूप है या एक लंबे समय की गणना करने के ले जा रहे हैं संभाल करने के लिए है।

इस से निपटने के कुछ इस तरह (के साथ और अधिक C# और कम स्यूडोकोड) के लिए किया जाएगा का एक अन्य संभावित रास्ता

Class ActionThread { 
    pulbic Thread thread { get; set; } 
    public Queue<Action> queue { get; set; } 

    public void Run() { 
     while (true) { 
      queue.WaitOne(); 
      Act a = queue.dequeue(); 
      a(); 
     } 
    } 

Class foo { 
    main() { 
     .... 
     foreach(Bot b in Bots) { 
      ActionThread a = getActionThread(b.UniqueID); 
      NewGame del = b.NewGame; 
      a.queue.queue(del); 
     } 
     Thread.Sleep(1000); 
     foreach (ActionThread a in Threads) { 
      a.Suspend(); 
     } 
    } 
} 

नहीं स्पष्ट तरीका है, लेकिन यह काम करेगा। (मैं इस बारे में चिंता करूंगा कि पैरामीटर को कैसे पास किया जाए और बाद में वापसी मूल्यों को कैसे लें)।

[आगे संपादित करें]

मैं भी यकीन है कि क्या एक appdomain यह दिखता है मैं ऐसा कर सकता है से, है नहीं कर रहा हूँ, लेकिन यह नहीं कर सकते देखें कि यह कैसे मदद मिलेगी

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

मैं क्या मैंने कहीं धीरे-धीरे हो रही टास्क के साथ कर सकते हैं, को देखने के लिए कोशिश कर रहा हूँ।

मैं कैस क्या कर सकते हैं में पढ़ा होगा, तुम लोगों

धन्यवाद [अधिक संपादित करें]

मेरा सिर, दर्द होता है मैं अब और नहीं लगता है कि या कोड नहीं कर पा रहे। मैं बॉट प्रति एक समर्पित धागा के लिए एक संदेश पारित प्रणाली की स्थापना कर रहा हूँ और निलंबित कर देगा/उन धागे

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

+3

आप इसे यहां पूछना चाहेंगे: http://gamedev.stackexchange.com/ – jjnguy

+0

क्या आप उन्हें किसी अन्य एपडोमेन में चला सकते हैं? –

+5

@ जस्टिन, क्यों? थोड़ा सा रिकॉर्डिंग के साथ, यह सिर्फ एक प्रोग्रामिंग समस्या है। किसी थ्रेड पर विधि को कैसे कॉल करें, कुछ समय बाद इसे साफ़ करने में सक्षम होना। –

उत्तर

10

दुर्भाग्य से थ्रेड साफ़ को समाप्त करने के लिए कोई 100% सुरक्षित तरीका नहीं है, जैसा कि आप चाहते हैं।

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

ऐसा करने का एकमात्र साफ, सुरक्षित और स्वीकृत तरीका है प्रश्न में धागे का सहयोग प्राप्त करना और इसे अच्छी तरह से से पूछें। हालांकि, यदि आप कोड के नियंत्रण में हैं तो यह केवल 100% गारंटीकृत तरीका है। चूंकि आप नहीं हैं, यह नहीं होगा।

यहां समस्या है।

  • आप एक Thread.Abort करते हैं, तो आप एक असुरक्षित राज्य में appdomain छोड़ने का जोखिम। फाइलें खुली रहेंगी, नेटवर्क या डेटाबेस कनेक्शन खुले रहेंगे, कर्नेल ऑब्जेक्ट्स अमान्य स्थिति में छोड़े जा सकते हैं, आदि
  • भले ही आप थ्रेड को अपने एपडोमेन में रखें, और थ्रेड को रद्द करने के बाद एपडोमेन को फाड़ें, आप कर सकते हैं 100% सुरक्षित नहीं है कि आपकी प्रक्रिया के कारण भविष्य में समस्याएं नहीं होंगी।

चलिए देखते हैं कि सहयोग 100% क्यों नहीं होने वाला है।

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

हालांकि, यह अपवाद पकड़ा जा सकता है, और निगल लिया जा सकता है।

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

जो मैंने पहले ही कहा है कि आप नहीं कर सकते हैं।

हालांकि, एक तरीका है जो काम कर सकता है। आप अपनी प्रक्रिया में बॉट को जन्म दे सकते हैं, फिर समय समाप्त होने पर प्रक्रिया को मार दें। यह आपको सफलता का एक बड़ा मौका देगा क्योंकि कम से कम ऑपरेटिंग सिस्टम प्रक्रियाओं की मृत्यु के दौरान प्रबंधित होने वाले सभी संसाधनों का ख्याल रखेगा। आप निश्चित रूप से सिस्टम पर दूषित फाइलों को छोड़ सकते हैं, फिर से, यह 100% साफ नहीं है।

यहां जो डफी द्वारा एक ब्लॉग प्रविष्टि है जो इन समस्याओं के बारे में बहुत कुछ बताती है: Managed code and asynchronous exception hardening

+0

ठीक है, तो 'कार्य' का उपयोग क्यों न करें? –

+0

क्योंकि यह सिर्फ एक ऑब्जेक्ट रैपर है, आपने अंतर्निहित समस्या हल नहीं की है जिसे आप .NET में थ्रेड को साफ़ नहीं कर सकते हैं। धागे से अच्छी तरह से पूछना एकमात्र सुरक्षित तरीका है, और इसे बाहर निकलने के लिए छोड़ दें। यह ऐसा नहीं करता है, या नहीं करेगा, ऐसा करने के लिए आपके पास कोई विकल्प नहीं बचा है। –

+0

मुझे लगता है कि हमें उम्मीद की जा रही असंगतता के स्तर को परिभाषित करने की आवश्यकता है। क्या हम दुर्भावनापूर्ण कोड या बस धीमे कोड के बारे में चिंतित हैं? –

4

एक .NET 4.0 Task आप रद्द करने के संबंध में काफी लचीलापन प्रदान करता है।

Task में प्रतिनिधि को चलाएं कि आपने को this पर पास किया है।

एक संपूर्ण उदाहरण here नहीं है।

संपादित

प्रतिक्रिया के प्रकाश में, मुझे विश्वास है कि सही जवाब इस योजना का पालन करने के लिए है:

  1. सीमा ऐ कैस का उपयोग कर, इतना है कि यह सूत्रण उपयोग नहीं कर सकते प्राइमेटिव्स या फाइलों के साथ गड़बड़।

  2. इसे दिल की धड़कन कॉलबैक दें कि यह लंबे लूप के अंदर से कॉल करने के लिए नैतिक रूप से बाध्य है। अगर थ्रेड बहुत लंबा लिया गया है तो यह कॉलबैक अपवाद फेंक देगा।

  3. यदि एआई बार बाहर निकलता है, तो इसके लिए एक कमजोर कदम लॉग करें, और कॉलबैक को कॉल करने के लिए इसे थोडा समय दें। यदि ऐसा नहीं होता है, तो थ्रेड को अपनी अगली बारी तक सोएं। यदि यह कभी भी बाहर नहीं निकलता है, तो यह कमजोर चालों को लॉगिंग बनाए रखेगा जब तक कि प्रक्रिया पृष्ठभूमि थ्रेड के लिए इसे मार न दे।

  4. लाभ!

+0

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

+0

मदद नहीं करता है, रद्दीकरण अभी भी कार्य द्वारा स्वैच्छिक है। –

+1

ठीक है, मैं अपना जवाब संशोधित कर दूंगा: प्रतिनिधि को एक अलग मशीन पर चलाएं, जिनकी बिजली आपूर्ति आईपी-एड्रेसेबल पावर स्ट्रिप से जुड़ी है। यदि यह टाइमआउट से पहले प्रतिक्रिया नहीं देता है, तो हार्ड रीसेट करें। बेहतर? –

2

एक विकल्प निश्चित रूप से अपने आप को एक नया Thread ऊपर स्पिन, बल्कि BeginInvoke पर भरोसा करने की बजाय है। आप थ्रेड के माध्यम से टाइमआउट को कार्यान्वित कर सकते हैं। जॉइन और (अनजाने में) थ्रेड को मार दें, यदि आवश्यक हो, तो थ्रेड.एबॉर्ट के माध्यम से।

+0

ठीक है, लेकिन 'थ्रेड.एबॉर्ट' का उपयोग करना एक अच्छा विचार नहीं है यदि आप संभवतः इससे बच सकते हैं। जिज्ञासा से –

+0

- आपके धागे को निरस्त करने में क्या गलत है (और संभवतः 'थ्रेडएबॉर्ट अपवाद' को साफ़ करने के लिए)? –

+0

बस thread.abort के लिए SO खोजें, आपको बहुत सारी पढ़ाई सामग्री मिलनी चाहिए। इसे समेटने के लिए, धागा किसी भी समय हो सकता है, थ्रेड के संबंध में, और यह निष्पादन पथ के वर्तमान बिंदु पर अपवाद से निपटने के लिए तैयार नहीं हो सकता है, क्योंकि केवल इसके लिए कोई सामान्य तरीका नहीं है उस बिंदु पर होने का अपवाद, इसलिए कोई असफलता नहीं है। उदाहरण के लिए, यदि स्ट्रीम ऑब्जेक्ट को बंद करने का मौका मिलने से पहले, 'अंत {}' ब्लॉक के अंदर अपवाद सही होता है तो क्या होगा। –

2

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

यह कहा गया है कि, यदि आप प्रत्येक बॉट सीएएस के साथ क्या करते हैं, तो आप अपने सिस्टम को दूषित करने के डर के बिना धागे को समाप्त करने में सक्षम हो सकते हैं। यदि एक बॉट एक अनंत लूप में फंस गया है, तो मुझे संदेह है कि आपका एकमात्र सहारा थ्रेड को समाप्त कर देगा क्योंकि यह प्रोग्रामिंग रूप से किसी भी कोड तक पहुंचने में सक्षम नहीं होगा जो थ्रेड के लिए जांच करेगा। एबॉर्ट सिग्नल।

This MSDN article आपको टर्मिनेट थ्रेड फ़ंक्शन का उपयोग करके रन-थ्रू थ्रेड को अधिक प्रभावी ढंग से समाप्त करने के तरीके पर कुछ मार्गदर्शन दे सकता है।

आपको वास्तव में इन सभी चीजों को आजमाएं और साबित करें कि यह सबसे अच्छा समाधान हो सकता है।

वैकल्पिक रूप से, यदि यह एक प्रतिस्पर्धी गेम और बॉट लेखकों को निष्पक्ष खेलना है, तो क्या आपने यह माना है कि गेम प्रत्येक बॉट को "टर्न टोकन" देता है जिसे बॉट को प्रत्येक क्रिया के साथ प्रस्तुत करना होता है जिसे वह आमंत्रित करना चाहता है और कब खेल फ्लिप बदल जाता है, यह सभी बॉट एक नया टोकन देता है। यह आपको केवल रन-थ्रू थ्रेड के बारे में चिंता करने की अनुमति दे सकता है, न कि रोबोट जो उनकी बारी पर लंबे समय तक लेते हैं। लोग जाने के लिए देखने के Security Permission Flag Enumerations तुम कहाँ शुरू करने के लिए की भावना दे देंगे

संपादित

बस एक जगह जोड़ने के लिए। आप SecurityPermissionFlag.ControlThread अनुमति निकालते हैं (एक उदाहरण के रूप में, केवल निष्पादित करने के लिए कोड की अनुमति देने और ControlThread को छोड़कर) आप अपने

धागे पर कुछ उन्नत संचालन के उपयोग की क्षमता को हटा देगा।

मुझे उन परिचालनों की सीमा नहीं पता है जो पहुंच योग्य नहीं हैं, लेकिन सप्ताहांत के लिए यह एक दिलचस्प अभ्यास होगा।

+0

'Thread.Abort' एक अनंत लूप को रोक देगा, हालांकि आपके 'AppDomain' को अस्थिर करने की लागत पर। –

+0

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

+0

धीरे, हां। यदि आप अप्रबंधित संसाधनों को प्राप्त करने या किसी भी चीज़ पर अवरुद्ध करने की क्षमता को हटा सकते हैं, हालांकि, 'थ्रेड.एबॉर्ट' ऐप किलर से कम हो जाता है। – cHao

0

एक और डिज़ाइन विकल्प प्रत्येक बॉट को अपनी प्रक्रिया होने की अनुमति देने के लिए हो सकता है, फिर डेटा का आदान-प्रदान करने के लिए आईपीसी तंत्र का उपयोग करें। आप आमतौर पर बिना किसी भयानक प्रतिक्रिया के प्रक्रिया को समाप्त कर सकते हैं।

+0

यह मेरी पसंद – Raynos

+0

के लिए बहुत भारी वजन लगता है यह पहले से ही सुझाव दिया गया था। –

+0

मैंने एक -1 जोड़ा क्योंकि यह एकजुट है। –

1

मुझे लगता है कि एकमात्र समस्या एक उलटा सशर्त है। कार्य को सफलतापूर्वक पूरा होने पर आपको EndInvoke पर कॉल करना चाहिए।

असहज सुझाव:

var goodBots = new List<IBot>(); 
var results = new IAsyncResult[Bots.Count]; 
var events = new WaitHandle[Bots.Count]; 
int i = 0; 
foreach (IBot bot in Bots) { 
    NewGame del = bot.NewGame; 
    results[i] = del.BeginInvoke(Info, null, null); 
    events[i++] = r.AsyncWaitHandle; 
} 
WaitAll(events, RoundLimit); 
var goodBots = new List<IBot>(); 
for(i = 0; i < events.Count; i++) { 
    if (results[i].IsCompleted) { 
     goodBots.Add(Bots[i]); 
     EndInvoke(results[i]); 
     results[i].Dispose(); 
    } 
    else { 
     WriteLine("bot " + i.ToString() + " eliminated by timeout."); 
    } 
} 
Bots = goodBots; 

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

+1

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

+0

@ रेनॉस: यह बहुत मजेदार है। असल में, मुझे लगता है कि अगर वे रद्दीकरण की सहकारी जांच करने से इनकार करते हैं तो यह उचित निष्पक्षता है। –

+0

धागे को निलंबित करने, आईआईआरसी, जो कुछ भी उन्होंने लॉक किया था उसे छोड़ देता है। जब तक आप अपने ऐप को फ्रीज नहीं करना चाहते हैं, तब तक आपको उन्हें ताले हासिल करने से रोकने की आवश्यकता होगी। और धागे को फिर से शुरू करना इसे पुनरारंभ नहीं करेगा; आप इसे अभी शुरू कर देंगे जहां यह छोड़ा गया था (संभावित रूप से पुराने डेटा और यहां तक ​​कि ऑब्जेक्ट्स के साथ जो धावक अब और उपयोग नहीं कर रहा है)। – cHao