我正在用Python3编程服务器,该服务器获取屏幕截图并将其通过websocket发送。我有协程用于处理连接,并且我想创建另一个协程以在一定时间间隔拍摄屏幕截图。屏幕快照协程可能会在不同的线程中运行,我将需要将结果传播到具有读写锁定的共享变量中,以便能够发送该结果。我的问题:(如果可能,结果应为多平台)
如何安排这样的任务?我创建了永远运行的服务器,并且可以创建定期的协程,但是由于某种原因我无法将它们组合在一起。
将结果从一个线程(或协程,如果服务器是单线程)传播到另一个线程的好方法是什么?
我发现这段代码与此类似,但我无法使其正常工作(第二协程未执行)。有人可以在没有多线程的情况下进行纠正吗?
async def print_var():
global number
await asyncio.sleep(2)
print(number)
async def inc_var():
global number
await asyncio.sleep(5)
number += 1
number = 0
asyncio.get_event_loop().run_until_complete(print_var())
asyncio.async(inc_var)
asyncio.get_event_loop().run_forever()
答案后编辑
最终,经过更多小时的谷歌搜索,我实际上使它可以在单个线程上工作,因此没有竞争状况的危险。 (但我仍然不确定sure_future的作用,以及为什么不在事件循环中调用它。)
users = set()
def register(websocket):
users.add(websocket)
def unregister(websocket):
users.remove(websocket)
async def get_screenshot():
global screenshot
while True:
screenshot = screenshot()
await asyncio.sleep(0.2)
async def server(websocket, path):
global screenshot
register(websocket)
try:
async for message in websocket:
respond(screenshot)
finally:
unregister(websocket)
def main():
asyncio.get_event_loop().run_until_complete(
websockets.serve(server, 'localhost', 6789))
asyncio.ensure_future(get_screenshot())
asyncio.get_event_loop().run_forever()
main()
答案 0 :(得分:0)
在Python 3.7中:
import asyncio
import websockets
CAPTURE_INTERVAL = 1
running = True
queues = set()
async def handle(ws, path):
queue = asyncio.Queue()
queues.add(queue)
while running:
data = await queue.get()
if not data:
break
await ws.send(data)
def capture_screen():
# Do some work here, preferably in C extension without holding the GIL
return b'screenshot data'
async def main():
global running
loop = asyncio.get_running_loop()
server = await websockets.serve(handle, 'localhost', 8765)
try:
while running:
data = await loop.run_in_executor(None, capture_screen)
for queue in queues:
queue.put_nowait(data)
await asyncio.sleep(CAPTURE_INTERVAL)
finally:
running = False
for queue in queues:
queue.put_nowait(None)
server.close()
await server.wait_closed()
if __name__ == '__main__':
asyncio.run(main())
请注意,这仅用于演示生产者-消费者扇出模式。 queues
不是必需的-您可以直接将data
发送到server.sockets
中的所有main()
,而在handle()
中则需要担心传入的Websocket消息。例如,客户端可以像这样控制图像压缩率:
import asyncio
import websockets
CAPTURE_INTERVAL = 1
DEFAULT = b'default'
qualities = {}
async def handle(ws, path):
try:
async for req in ws:
qualities[ws] = req
finally:
qualities.pop(ws, None)
def capture_screen():
# Do some work here, preferably in C extension without holding the GIL
return {
DEFAULT: b'default screenshot data',
b'60': b'data at 60% quality',
b'80': b'data at 80% quality',
}
async def main():
loop = asyncio.get_running_loop()
server = await websockets.serve(handle, 'localhost', 8765)
try:
while True:
data = await loop.run_in_executor(None, capture_screen)
for ws in server.sockets:
quality = qualities.get(ws, DEFAULT)
if quality not in data:
quality = DEFAULT
asyncio.create_task(ws.send(data[quality]))
await asyncio.sleep(CAPTURE_INTERVAL)
finally:
server.close()
await server.wait_closed()
if __name__ == '__main__':
asyncio.run(main())