2016-03-10 10 views
7

を保留している:Pythonはasyncio - ループタスクで終了し破壊されたが、これが私のPythonプログラムの関連するコードです

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でPython 3.4.3+を持っています。

答えて

2

をこれがあります。

これは、一定時間以上コントロールを盗む機能があれば、discordのクライアントが無効な状態になることを意味します(おそらく、クライアントの次のメソッド呼び出し時に例外が現れます)。

discordモジュールクライアントがdiscordサーバーにpingできるようにするには、真のマルチスレッドソリューションを使用する必要があります。

重大な処理を別のプロセスにオフロードすることです(Pythonにはグローバルインタプリタロックがあるため、別のスレッドは実行しません)。そして、この不和ボットを作業キューに入れる責任がある薄いレイヤーとして使用します。

関連読書: 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) 

これは、重い要求を処理するためのワーカーサーバーです。この部分を同期または非同期にすることができます。

1つのpythonプロセスから別のプロセスにデータを送るために、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() 

あなたは別のPythonスクリプトで両方のプロセスを実行して、あなたのボット「100のための睡眠」を伝える場合、それは喜んで100秒間スリープします! asyncio stuffはmake-shift作業キューとして機能し、別々のpythonスクリプトとして実行することで、リスナーをバックエンド処理から適切に分離することができます。

ここで、あなたの機能が「サーバー」の部分でどれくらい実行されても、クライアントのパーツはディスパードサーバーにpingを実行することは決してありません。

画像がとにかくアップロードに失敗した、しかし...、これは睡眠が同期していることに注意してください...寝て返信するボットに伝える方法です。 http://i.imgur.com/N4ZPPbB.png

+0

不和の仕組みについて、この洞察をいただきありがとうございます。残念ながら、私はスレッド型プログラミングと非同期プログラミングの専門家ではありません。この「薄い層」が何であるか、そしてそれをどう実装するかをもう少し説明できますか? – shrx

+0

は多くの方法がありますが、その議論は芋この質問の範囲を超えています。私は私の個人的なコードをブラウズします(それはかなり悪い...私はotを書いたので)、私はあなたのためにいくつかのスニペットとアイデアを抽出できるかどうかを見てください:) – 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

私のプログラムは無期限に実行し、毎日(特に)doSomething()関数を1回実行する必要があります。 – 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

関連する問題