2016-03-10 10 views
7

लंबित है यह मेरा अजगर कार्यक्रम के प्रासंगिक कोड है: कोई अन्य के साथअजगर asyncio - लूप बाहर निकालता टास्क के साथ नष्ट हो गया था, लेकिन यह

import discord 
import asyncio 

class Bot(discord.Client): 
    def __init__(self): 
     super().__init__() 

    @asyncio.coroutine 
    def my_background_task(self): 
     yield from self.wait_until_ready() 
     while not self.is_closed: 
      yield from asyncio.sleep(3600*24) # <- This is line 76 where it fails 
      doSomething() 

bot = Bot() 
loop = asyncio.get_event_loop() 
try: 
    loop.create_task(bot.my_background_task()) 
    loop.run_until_complete(bot.login('username', 'password')) 
    loop.run_until_complete(bot.connect()) 
except Exception: 
    loop.run_until_complete(bot.close()) 
finally: 
    loop.close() 

कार्यक्रम कभी कभी इस्तीफा (अपने आप ही है, जबकि ऐसा नहीं होना चाहिए) त्रुटियों या कैसे कार्यक्रम बेतरतीब ढंग से छोड़ नहीं होगा सुनिश्चित करने के लिए

Task was destroyed but it is pending! 
task: <Task pending coro=<my_background_task() running at bin/discordBot.py:76> wait_for=<Future pending cb=[Task._wakeup()]>> 

के अलावा अन्य चेतावनी? मैं Xubuntu 15.10 पर पायथन 3.4.3+ है।

उत्तर

2

ऐसा इसलिए है क्योंकि विवाद क्लाइंट मॉड्यूल को हर मिनट या तो एक बार नियंत्रण की आवश्यकता होती है।

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

यह सुनिश्चित करने के लिए कि विवाद मॉड्यूल क्लाइंट डिस्कॉर्ड सर्वर को पिंग कर सकता है, आपको एक वास्तविक बहु-थ्रेडिंग समाधान का उपयोग करना चाहिए।

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

संबंधित पढ़ने: https://discordpy.readthedocs.io/en/latest/faq.html#what-does-blocking-mean

उदाहरण समाधान ... इस समस्या के दायरे से बाहर तरीका है, लेकिन मैं पहले से ही कोड ज्यादातर लिखा था।

इस कलह श्रोता है: यदि मैं अधिक समय था, मैं एक छोटी समाधान :)

2 भागों, कलह संपर्क और संसाधन सर्वर लिखेंगे।

import discord 
import re 
import asyncio 
import traceback 

import websockets 
import json 

# Call a function on other server 
async def call(methodName, *args, **kwargs): 
    async with websockets.connect('ws://localhost:9001/meow') as websocket: 
     payload = json.dumps({"method":methodName, "args":args, "kwargs": kwargs}) 
     await websocket.send(payload) 
     #... 
     resp = await websocket.recv() 
     #... 
     return resp 

client = discord.Client() 
tok = open("token.dat").read() 

@client.event 
async def on_ready(): 
    print('Logged in as') 
    print(client.user.name) 
    print(client.user.id) 
    print('------') 

@client.event 
async def on_error(event, *args, **kwargs): 
    print("Error?") 

@client.event 
async def on_message(message): 
    try: 
     if message.author.id == client.user.id: 
      return 
     m = re.match("(\w+) for (\d+).*?", message.content) 
     if m: 
      g = m.groups(1) 
      methodName = g[0] 
      someNumber = int(g[1]) 
      response = await call(methodName, someNumber) 
      if response: 
       await client.send_message(message.channel, response[0:2000]) 
    except Exception as e: 
     print (e) 
     print (traceback.format_exc()) 

client.run(tok) 

भारी अनुरोधों को संसाधित करने के लिए यह कार्यकर्ता सर्वर है। आप इस हिस्से सिंक या async बना सकते हैं।

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

import tornado 
import tornado.websocket 
import tornado.httpserver 
import json 
import asyncio 
import inspect 
import time 

class Handler: 
    def __init__(self, *args, **kwargs): 
     super().__init__(*args, **kwargs) 

    def consume(self, text): 
     return "You said {0} and I say hiya".format(text) 

    async def sweeps(self, len): 
     await asyncio.sleep(len) 
     return "Slept for {0} seconds asynchronously!".format(len) 

    def sleeps(self, len): 
     time.sleep(len) 
     return "Slept for {0} seconds synchronously!".format(len) 


class MyService(Handler, tornado.websocket.WebSocketHandler): 
    def __init__(self, *args, **kwargs): 
     super().__init__(*args, **kwargs) 

    def stop(self): 
     Handler.server.stop() 

    def open(self): 
     print("WebSocket opened") 

    def on_message(self, message): 
     print (message) 
     j = json.loads(message) 
     methodName = j["method"] 
     args = j.get("args",()) 

     method = getattr(self, methodName) 
     if inspect.iscoroutinefunction(method): 
      loop = asyncio.get_event_loop() 
      task = loop.create_task(method(*args)) 
      task.add_done_callback(lambda res: self.write_message(res.result())) 
      future = asyncio.ensure_future(task) 

     elif method: 
      resp = method(*args) 
      self.write_message(resp) 

    def on_close(self): 
     print("WebSocket closed") 

application = tornado.web.Application([ 
    (r'/meow', MyService), 
]) 

if __name__ == "__main__": 
    from tornado.platform.asyncio import AsyncIOMainLoop 
    AsyncIOMainLoop().install() 

    http_server = tornado.httpserver.HTTPServer(application) 
    Handler.server = http_server 
    http_server.listen(9001) 

    asyncio.get_event_loop().run_forever() 

अब, अगर आप अलग अजगर स्क्रिप्ट में दोनों प्रक्रियाओं को चलाने के लिए, और अपने बॉट "100 के लिए नींद" बताओ, यह 100 सेकंड के लिए खुशी से सो जाएगा! एक अस्थायी काम कतार के रूप में asyncio सामान काम करता है, और आप ठीक ढंग से उन्हें अलग अजगर स्क्रिप्ट के रूप में चलाकर बैकेंड संसाधन से श्रोता अलग कर सकते हैं।

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

छवि अपलोड करने में विफल रही, लेकिन ... वैसे भी, यह बॉट को सोने और जवाब देने के बारे में बताने के लिए है ... ध्यान दें कि नींद तुल्यकालिक है। http://i.imgur.com/N4ZPPbB.png

+0

विवाद के कार्यकलापों में इस अंतर्दृष्टि के लिए धन्यवाद। दुर्भाग्य से मैं थ्रेडेड और एसिंक्रोनस प्रोग्रामिंग में एक विशेषज्ञ नहीं हूं। क्या आप थोड़ा और समझा सकते हैं कि यह "पतली परत" क्या है और इसे कैसे कार्यान्वित किया जाए? – shrx

+0

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

+0

उदाहरण के साथ पूरी तरह से स्पष्टीकरण के लिए धन्यवाद। – shrx

0

आप मैन्युअल रूप से बाहर निकलने पर अपने कार्य को रोकने के लिए:

import discord 
import asyncio 

class Bot(discord.Client): 
    def __init__(self): 
     super().__init__() 

    @asyncio.coroutine 
    def my_background_task(self): 
     yield from self.wait_until_ready() 
     while not self.is_closed: 
      yield from asyncio.sleep(3600*24) # <- This is line 76 where it fails 
      doSomething() 

bot = Bot() 
loop = asyncio.get_event_loop() 
try: 
    task = loop.create_task(bot.my_background_task()) 
    loop.run_until_complete(bot.login('username', 'password')) 
    loop.run_until_complete(bot.connect()) 
except Exception: 
    loop.run_until_complete(bot.close()) 
finally: 
    task.cancel() 
    try: 
     loop.run_until_complete(task) 
    except Exception: 
     pass 
    loop.close() 
+0

मेरा प्रोग्राम बाहर निकलना नहीं है, इसे अनिश्चित काल तक चलाना चाहिए और हर दिन एक बार (कुछ चीजों के साथ) काम करना चाहिए। – shrx

+0

लेकिन आपका प्रोग्राम निश्चित रूप से पृष्ठभूमि कार्य को पूर्ण रूप से पूरा नहीं करता है, जो ईवेंटलोप बंद करने पर चेतावनी पाठ उत्पन्न करता है। मैं इसे रोकने के लिए उचित पृष्ठभूमि कार्य रद्दीकरण का सुझाव देता हूं। –

+0

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

1

मुझे नहीं लगता जबकि asyncio.sleep समस्या होता है। वैसे भी आपको अपवाद को दबाना नहीं चाहिए:

bot = Bot() 
loop = asyncio.get_event_loop() 
try: 
    # ... 
except Exception as e: 
    loop.run_until_complete(bot.close()) 
    raise e # <--- reraise exception you got while execution to see it (or log it here) 
finally: 
    # ... 
+1

हालांकि यह सवाल का जवाब देता है, मुझे संदेह है कि इस समस्या में वास्तव में समस्या ठीक से उत्पन्न नहीं हुई है। मैं प्रश्नकर्ता के मुंह में शब्दों को डालकर बहुत दूर नहीं जाना चाहता, लेकिन मुझे लगता है कि वास्तव में क्या पूछा गया था "यह सुनिश्चित करने के लिए कि प्रोग्राम यादृच्छिक रूप से बाहर नहीं निकलेगा?" इस तरह के सोने के सवाल के दायरे को सीमित किए बिना। – JamEnergy

+0

@JamEnergy आप सही हैं, मैंने सवाल संपादित किया है। – shrx

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