- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在编写一个程序,该程序通过 AMQP 接受 RPC 请求以执行网络请求 (CoAP)。在处理 RPC 请求时,aioamqp 回调会生成负责网络 IO 的任务。这些任务可以被视为后台任务,它们将无限期运行以通过 AMQP 流式传输网络响应(在这种情况下,一个 RPC 请求会触发 RPC 响应和数据流)。
我注意到在我的原始代码中,网络任务将在看似随机的时间间隔后(在完成之前)被销毁,然后 asyncio 会打印以下警告“任务已被销毁但它正在挂起”。此问题类似于此处描述的问题:https://bugs.python.org/issue21163 .
现在我通过在模块级列表中存储硬引用来规避这个问题,这可以防止 GC 破坏任务对象。但是,我想知道是否有更好的解决方法?理想情况下,我想在 RPC 回调中调用 await 任务,但我注意到这会阻止任何进一步的 AMQP 操作完成 -> 例如创建一个新的 amqp channel 会停止并且通过 amqp 接收 rpc 请求也会停止。但是,我不确定是什么导致了这种停顿(因为回调本身就是一个协程,我希望等待不会停顿整个 aioamqp 库)。
我在下面发布了 RPC 客户端和服务器的源代码,它们都基于 aioamqp/aiocoap 示例。在服务器中,on_rpc_request 是 amqp rpc 回调,而 send_coap_obs_request 是网络协程,当 'obs_tasks.append(task)' 语句被删除时,它会被销毁。
客户端.py:
"""
CoAP RPC client, based on aioamqp implementation of RPC examples from RabbitMQ tutorial
"""
import base64
import json
import uuid
import asyncio
import aioamqp
class CoAPRpcClient(object):
def __init__(self):
self.transport = None
self.protocol = None
self.channel = None
self.callback_queue = None
self.waiter = asyncio.Event()
async def connect(self):
""" an `__init__` method can't be a coroutine"""
self.transport, self.protocol = await aioamqp.connect()
self.channel = await self.protocol.channel()
result = await self.channel.queue_declare(queue_name='', exclusive=True)
self.callback_queue = result['queue']
await self.channel.basic_consume(
self.on_response,
no_ack=True,
queue_name=self.callback_queue,
)
async def on_response(self, channel, body, envelope, properties):
if self.corr_id == properties.correlation_id:
self.response = body
self.waiter.set()
async def call(self, n):
if not self.protocol:
await self.connect()
self.response = None
self.corr_id = str(uuid.uuid4())
await self.channel.basic_publish(
payload=str(n),
exchange_name='',
routing_key='coap_request_rpc_queue',
properties={
'reply_to': self.callback_queue,
'correlation_id': self.corr_id,
},
)
await self.waiter.wait()
await self.protocol.close()
return json.loads(self.response)
async def rpc_client():
coap_rpc = CoAPRpcClient()
request_dict = {}
request_dict_json = json.dumps(request_dict)
print(" [x] Send RPC coap_request({})".format(request_dict_json))
response_dict = await coap_rpc.call(request_dict_json)
print(" [.] Got {}".format(response_dict))
asyncio.get_event_loop().run_until_complete(rpc_client())
"""
CoAP RPC server, based on aioamqp implementation of RPC examples from RabbitMQ tutorial
"""
import base64
import json
import sys
import logging
import warnings
import asyncio
import aioamqp
import aiocoap
amqp_protocol = None
coap_client_context = None
obs_tasks = []
AMQP_COAP_NOTIFICATIONS_EXCHANGE_NAME = 'topic_coap'
AMQP_COAP_NOTIFICATIONS_TOPIC_NAME = 'topic'
AMQP_COAP_NOTIFICATIONS_ROUTING_KEY = 'coap.response'
def create_response_dict(coap_request, coap_response):
response_dict = {'request_uri': "", 'code': 0}
response_dict['request_uri'] = coap_request.get_request_uri()
response_dict['code'] = coap_response.code
if len(coap_response.payload) > 0:
response_dict['payload'] = base64.b64encode(coap_response.payload).decode('utf-8')
return response_dict
async def handle_coap_response(amqp_envelope, amqp_properties, coap_request, coap_response):
# create response dict:
response_dict = create_response_dict(coap_request, coap_response)
message = json.dumps(response_dict)
# create new channel:
global amqp_protocol
amqp_channel = await amqp_protocol.channel()
await amqp_channel.basic_publish(
payload=message,
exchange_name='',
routing_key=amqp_properties.reply_to,
properties={
'correlation_id': amqp_properties.correlation_id,
},
)
await amqp_channel.basic_client_ack(delivery_tag=amqp_envelope.delivery_tag)
print(" [.] handle_coap_response() published response: {}".format(response_dict))
def incoming_observation(coap_request, coap_response):
asyncio.async(handle_coap_notification(coap_request, coap_response))
async def handle_coap_notification(coap_request, coap_response):
# create response dict:
response_dict = create_response_dict(coap_request, coap_response)
message = json.dumps(response_dict)
# create new channel:
global amqp_protocol
amqp_channel = await amqp_protocol.channel()
await amqp_channel.exchange(AMQP_COAP_NOTIFICATIONS_EXCHANGE_NAME, AMQP_COAP_NOTIFICATIONS_TOPIC_NAME)
await amqp_channel.publish(message, exchange_name=AMQP_COAP_NOTIFICATIONS_EXCHANGE_NAME, routing_key=AMQP_COAP_NOTIFICATIONS_ROUTING_KEY)
print(" [.] handle_coap_notification() published response: {}".format(response_dict))
async def send_coap_obs_request(amqp_envelope, amqp_properties, request_dict, coap_request):
observation_is_over = asyncio.Future()
try:
global coap_client_context
requester = coap_client_context.request(coap_request)
requester.observation.register_errback(observation_is_over.set_result)
requester.observation.register_callback(lambda data, coap_request=coap_request: incoming_observation(coap_request, data))
try:
print(" [..] Sending CoAP obs request: {}".format(request_dict))
coap_response = await requester.response
except socket.gaierror as e:
print("Name resolution error:", e, file=sys.stderr)
return
except OSError as e:
print("Error:", e, file=sys.stderr)
return
if coap_response.code.is_successful():
print(" [..] Received CoAP response: {}".format(coap_response))
await handle_coap_response(amqp_envelope, amqp_properties, coap_request, coap_response)
else:
print(coap_response.code, file=sys.stderr)
if coap_response.payload:
print(coap_response.payload.decode('utf-8'), file=sys.stderr)
sys.exit(1)
exit_reason = await observation_is_over
print("Observation is over: %r"%(exit_reason,), file=sys.stderr)
finally:
if not requester.response.done():
requester.response.cancel()
if not requester.observation.cancelled:
requester.observation.cancel()
async def on_rpc_request(amqp_channel, amqp_body, amqp_envelope, amqp_properties):
print(" [.] on_rpc_request(): received RPC request: {}".format(amqp_body))
request_dict = {} # hardcoded to vdna.be for SO example
aiocoap_code = aiocoap.GET
aiocoap_uri = "coap://vdna.be/obs"
aiocoap_payload = ""
# as we are ready to send the CoAP request, ack the client already indicating we have received the RPC request
await amqp_channel.basic_client_ack(delivery_tag=amqp_envelope.delivery_tag)
coap_request = aiocoap.Message(code=aiocoap_code, uri=aiocoap_uri, payload=aiocoap_payload)
coap_request.opt.observe = 0
task = asyncio.ensure_future(send_coap_obs_request(amqp_envelope, amqp_properties, request_dict, coap_request))
# we have to keep a hard ref to this task, otherwise the python garbage collector destroyes the task before it is completed. See https://bugs.python.org/issue21163
# this is apparent from the "Task was destroyed but it is pending" exception thrown after random (lengthy) time intervals, probably the time interval is related to when the gc is triggered
# await task # this does not seem to work, as it prevents new amqp operations from executing (e.g. amqp channels do not get created)
# we are actually not interested in waiting for the task anyway, so instead just keep a hard ref to the task in the obs_tasks list
obs_tasks.append(task) # TODO: when do we remove the task from the list?
async def amqp_connect():
try:
(transport, protocol) = await aioamqp.connect('localhost', 5672)
print(" [x] Connected to AMQP broker")
return (transport, protocol)
except aioamqp.AmqpClosedConnection as ex:
print("closed connections: {}".format(ex))
raise ex
async def main():
"""Open AMQP connection to broker, subscribe to coap_request_rpc_queue and setup aiocoap client context """
try:
global amqp_protocol
(amqp_transport, amqp_protocol) = await amqp_connect()
channel = await amqp_protocol.channel()
await channel.queue_declare(queue_name='coap_request_rpc_queue')
await channel.basic_qos(prefetch_count=10, prefetch_size=0, connection_global=False)
await channel.basic_consume(on_rpc_request, queue_name='coap_request_rpc_queue')
print(" [x] Awaiting CoAP request RPC requests")
except aioamqp.AmqpClosedConnection as ex:
print("amqp_connect: closed connections: {}".format(ex))
exit()
global coap_client_context
coap_client_context = await aiocoap.Context.create_client_context()
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.set_debug(True)
asyncio.async(main())
loop.run_forever()
最佳答案
当一个任务被调度时,它是 _step
回调被安排在循环中。该回调通过 self
维护对任务的引用。 .我没有检查过代码,但我非常确信循环会维护对其回调的引用。但是,当任务等待某个可等待的或 future 的时,_step
未安排回调。在这种情况下,任务会添加一个完成回调,该回调保留对任务的引用,但循环不保留对等待 future 的任务的引用。
只要某些东西保留了对任务正在等待的 future 的引用,一切都很好。然而,如果没有任何东西保留对 future 的硬引用,那么 future 可以被垃圾收集,当发生这种情况时,任务可以被垃圾收集。
因此,我会寻找您的任务调用的内容,而该任务正在等待的 future 可能不会被引用。
一般来说, future 需要被引用,以便有人最终可以设置它的结果,所以如果你有未引用的 future ,这很可能是一个错误。
关于Python 异步 : unreferenced tasks are destroyed by garbage collector?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44304603/
这个问题在这里已经有了答案: Why use async and return await, when you can return Task directly? (8 个答案) 关闭 6 年前。
这个问题在这里已经有了答案: Are the days of passing const std::string & as a parameter over? (13 个答案) 关闭 8 年前。 我
我有一组标记为执行的通用任务。当任务完成时(使用 Task.WaitAny ),我将其添加到 ObservableCollection 中. 但是,问题出在 Task.WaitAny(...)行,上面
经过几个小时的努力,我在我的应用程序中发现了一个错误。我认为下面的 2 个函数具有相同的行为,但事实证明它们没有。 谁能告诉我引擎盖下到底发生了什么,以及为什么它们的行为方式不同? public as
这也与 Python 的导入机制有关,特别是与在函数内使用 import 有关。使用 Python 2.7.9 和 Fabric 1.10.0,创建以下三个文件: fabfile.py: from a
我有一个 Web API Controller (ASP.NET Core 5)。我的一些 API 是异步的,而其中一些不是。我接下来的问题是:使用 public **Task** WebApiMet
我们有类似下面的内容 List uncheckItems = new List(); for (int i = 0; i new Task(async () => await Process
我的代码没问题,但我想知道哪种风格更好,你会怎么看,我正在玩异步方法。 让我建立上下文: Parallel.ForEach(xmlAnimalList, async xml => {
这两种使用 await 的形式在功能上有什么区别吗? string x = await Task.Factory.StartNew(() => GetAnimal("feline")); Task m
我刚刚看到 3 个关于 TPL 使用的例程,它们做同样的工作;这是代码: public static void Main() { Thread.CurrentThread.Name = "Ma
考虑以下代码: public void CacheData() { Task.Run((Action)CacheExternalData); Task.Run(() => CacheE
Task> GetTaskDict() { return Task.FromResult(new Dictionary () ); } 此代码无法编译,因为我们无法在 Task> 到 Tas
我正在使用 ASP.NET 5 RC1 _MyPartial @model MyViewModel @using (Html.BeginForm())
当我尝试在 VS Code 中构建 C 任务时,它显示以下消息: 输出仅显示:The task provider for "C/C++" tasks unexpectedly provided a t
一些背景: 基本上归结为我希望能够在当前线程中“执行”任务。为什么? -我有一个任务创建程序例程,有一次我希望任务在后台任务中立即执行,而其他时候我希望使用 IOmniThreadPool 安排任务。
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the he
我试图将run-sequence添加到我的gulp工作流程中,但是每次尝试执行使用run-sequence的任务时,都会出现此错误: 任务未配置为gulp上的任务。 根据运行序列的来源,这是由以下te
此代码在VS2015中给出了编译时错误 Error CS0266 Cannot implicitly convert type 'System.Threading.Tasks.Task' to 'Sy
我正在尝试通过我的代码通过Google登出: suspend fun signOut(context: Context): Boolean = with(Dispatchers.IO) { t
谁能解释一下这两种说法的区别: Task bTask = backup.BackupCurrentDatabaseAsync() .ContinueWith(_ => CompressArch
我是一名优秀的程序员,十分优秀!