只要有待处理的取消屏蔽任务留下但不再存在,我如何运行 asyncio 循环?

2024-04-08

我正在尝试向现有的 asyncio 循环添加一些代码,以提供 Ctrl-C 上的干净关闭。下面是它正在做的事情的抽象。

import asyncio, signal

async def task1():
    print("Starting simulated task1")
    await asyncio.sleep(5)
    print("Finished simulated task1")

async def task2():
    print("Starting simulated task2")
    await asyncio.sleep(5)
    print("Finished simulated task2")

async def tasks():
    await task1()
    await task2()

async def task_loop():
    try:
        while True:
            await asyncio.shield(tasks())
            await asyncio.sleep(60)
    except asyncio.CancelledError:
        print("Shutting down task loop")
        raise

async def aiomain():
    loop = asyncio.get_running_loop()
    task = asyncio.Task(task_loop())
    loop.add_signal_handler(signal.SIGINT, task.cancel)
    await task

def main():
    try:
        asyncio.run(aiomain())
    except asyncio.CancelledError:
        pass

#def main():
#    try:
#        loop = asyncio.get_event_loop()
#        loop.create_task(aiomain())
#        loop.run_forever()
#    except asyncio.CancelledError:
#        pass

if __name__ == '__main__':
    main()

在这个例子中,想象一下序列task1 and task2一旦开始就需要完成,否则某些工件将处于不一致的状态。 (因此asyncio.shield调用的包装器tasks.)

使用上面的代码,如果我在脚本启动后不久就中断它并且它只是打印出来Starting simulated task1然后循环停止task2永远不会开始。如果我尝试切换到的版本main然后注释掉,即使循环被正确取消并且至少在几分钟内没有进一步发生,也永远不会退出。它确实有一些进展,因为它至少完成了任何正在进行的序列task1 and task2.

头脑风暴中的一些可能的解决方案,尽管我仍然感觉一定有一些更简单的东西我错过了:

  • 创建一个包装器asyncio.shield它增加一个由同步的变量asyncio.Condition对象,运行屏蔽函数,然后递减变量。然后,在aiomain in a CancelledError处理程序,在重新引发异常之前等待变量达到零。 (在实现中,我可能会将其所有部分组合到一个类中__aexit__实施等待零CancelledError logic.)
  • 跳过使用asyncio完全取消机制,而是使用asyncio.Event或类似的允许中断点或可中断睡眠。尽管这看起来确实更具侵入性,要求我指定哪些点被认为是可中断的,而不是声明哪些序列需要避免取消。

这个问题问得好。我在制定答案时学到了一些东西,所以我希望您仍然关注这个帖子。

首先要研究的是,shield() 方法是如何工作的?在这一点上,文档至少可以说是令人困惑的。直到读了test_tasks.py中的标准库测试代码我才弄清楚。这是我的理解:

考虑这个代码片段:

async def coro_a():
    await asyncio.sheild(task_b())
    ...
task_a = asyncio.create_task(coro_a())
task_a.cancel()

当执行task_a.cancel()语句时,task_a确实被取消了。 wait 语句抛出 CancelledError立即地,无需等待task_b完成。但task_b继续运行。外部任务 (a) 停止,但内部任务 (b) 没有停止。

这是程序的修改版本,说明了这一点。主要的变化是在 CancelledError 异常处理程序中插入等待,以使程序的存活时间延长几秒钟。我在 Windows 上运行,这就是为什么我也稍微改变了你的信号处理程序,但这只是一个小问题。我还在打印语句中添加了时间戳。

import asyncio
import signal
import time

async def task1():
    print("Starting simulated task1", time.time())
    await asyncio.sleep(5)
    print("Finished simulated task1", time.time())

async def task2():
    print("Starting simulated task2", time.time())
    await asyncio.sleep(5)
    print("Finished simulated task2", time.time())

async def tasks():
    await task1()
    await task2()

async def task_loop():
    try:
        while True:
            await asyncio.shield(tasks())
            await asyncio.sleep(60)
    except asyncio.CancelledError:
        print("Shutting down task loop", time.time())
        raise

async def aiomain():
    task = asyncio.create_task(task_loop())
    KillNicely(task)
    try:
        await task
    except asyncio.CancelledError:
        print("Caught CancelledError", time.time())
        await asyncio.sleep(5.0)
        raise

class KillNicely:
    def __init__(self, cancel_me):
        self.cancel_me = cancel_me
        self.old_sigint = signal.signal(signal.SIGINT,
                                        self.trap_control_c)

    def trap_control_c(self, signum, stack):
        if signum != signal.SIGINT:
            self.old_sigint(signum, stack)
        else:
            print("Got Control-C", time.time())
            print(self.cancel_me.cancel())

def main():
    try:
        asyncio.run(aiomain())
    except asyncio.CancelledError:
        print("Program exit, cancelled", time.time())

# Output when ctrlC is struck during task1
# 
# Starting simulated task1 1590871747.8977509
# Got Control-C 1590871750.8385916
# True
# Shutting down task loop 1590871750.8425908
# Caught CancelledError 1590871750.8435903
# Finished simulated task1 1590871752.908434
# Starting simulated task2 1590871752.908434
# Program exit, cancelled 1590871755.8488846        

if __name__ == '__main__':
    main()

您可以看到您的程序无法运行,因为在task_loop 被取消后,在task1 和task2 有机会完成之前它就退出了。它们一直都在那里(或者更确切地说,如果程序继续运行,它们就会在那里)。

这说明了shield()和cancel()如何交互,但它实际上并没有解决您所说的问题。为此,我认为,您需要有一个可等待的对象,您可以使用它来使程序保持活动状态,直到重要任务完成。该对象需要在顶层创建,并将堆栈向下传递到执行重要任务的位置。这是一个与您的程序类似的程序,但按照您想要的方式执行。

我运行了 3 次:(1) 在任务 1 期间使用 control-C,(2) 在任务 2 期间使用 control-C,(3) 在两个任务完成后使用 control-C。在前两种情况下,程序会继续执行,直到任务 2 完成。在第三种情况下,它立即结束。

import asyncio
import signal
import time

async def task1():
    print("Starting simulated task1", time.time())
    await asyncio.sleep(5)
    print("Finished simulated task1", time.time())

async def task2():
    print("Starting simulated task2", time.time())
    await asyncio.sleep(5)
    print("Finished simulated task2", time.time())

async def tasks(kwrap):
    fut = asyncio.get_running_loop().create_future()
    kwrap.awaitable = fut
    await task1()
    await task2()
    fut.set_result(1)

async def task_loop(kwrap):
    try:
        while True:
            await asyncio.shield(tasks(kwrap))
            await asyncio.sleep(60)
    except asyncio.CancelledError:
        print("Shutting down task loop", time.time())
        raise

async def aiomain():
    kwrap = KillWrapper()
    task = asyncio.create_task(task_loop(kwrap))
    KillNicely(task)
    try:
        await task
    except asyncio.CancelledError:
        print("Caught CancelledError", time.time())
        await kwrap.awaitable
        raise

class KillNicely:
    def __init__(self, cancel_me):
        self.cancel_me = cancel_me
        self.old_sigint = signal.signal(signal.SIGINT,
                                        self.trap_control_c)

    def trap_control_c(self, signum, stack):
        if signum != signal.SIGINT:
            self.old_sigint(signum, stack)
        else:
            print("Got Control-C", time.time())
            print(self.cancel_me.cancel())

class KillWrapper:
    def __init__(self):
        self.awaitable = asyncio.get_running_loop().create_future()
        self.awaitable.set_result(0)

def main():
    try:
        asyncio.run(aiomain())
    except asyncio.CancelledError:
        print("Program exit, cancelled", time.time())

# Run 1 Control-C during task1
# Starting simulated task1 1590872408.6737766
# Got Control-C 1590872410.7344952
# True
# Shutting down task loop 1590872410.7354996
# Caught CancelledError 1590872410.7354996
# Finished simulated task1 1590872413.6747622
# Starting simulated task2 1590872413.6747622
# Finished simulated task2 1590872418.6750958
# Program exit, cancelled 1590872418.6750958
#
# Run 1 Control-C during task2
# Starting simulated task1 1590872492.927735
# Finished simulated task1 1590872497.9280624
# Starting simulated task2 1590872497.9280624
# Got Control-C 1590872499.5973852
# True
# Shutting down task loop 1590872499.5983844
# Caught CancelledError 1590872499.5983844
# Finished simulated task2 1590872502.9274273
# Program exit, cancelled 1590872502.9287038
#
# Run 1 Control-C after task2 -> immediate exit
# Starting simulated task1 1590873694.2925708
# Finished simulated task1 1590873699.2928336
# Starting simulated task2 1590873699.2928336
# Finished simulated task2 1590873704.2938952
# Got Control-C 1590873706.0790765
# True
# Shutting down task loop 1590873706.0804725
# Caught CancelledError 1590873706.0804725
# Program exit, cancelled 1590873706.0814824
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

只要有待处理的取消屏蔽任务留下但不再存在,我如何运行 asyncio 循环? 的相关文章

  • 从数据框中按索引删除行

    我有一个数组wrong indexes train其中包含我想从数据框中删除的索引列表 0 63 151 469 1008 要删除这些索引 我正在尝试这样做 df train drop wrong indexes train 但是 代码失败
  • 使用 python 进行串行数据记录

    Intro 我需要编写一个小程序来实时读取串行数据并将其写入文本文件 我在读取数据方面取得了一些进展 但尚未成功地将这些信息存储在新文件中 这是我的代码 from future import print function import se
  • 我怎样才能更多地了解Python的内部原理? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我使用Python编程已经有半年多了 我对Python内部更感兴趣 而不是使用Python开发应用程序
  • 如何迭代按值排序的 Python 字典?

    我有一本字典 比如 a 6 b 1 c 2 我想迭代一下by value 不是通过键 换句话说 b 1 c 2 a 6 最直接的方法是什么 sorted dictionary items key lambda x x 1 对于那些讨厌 la
  • Python逻辑运算符优先级[重复]

    这个问题在这里已经有答案了 哪个运算符优先4 gt 5 or 3 lt 4 and 9 gt 8 这会被评估为真还是假 我知道该声明3 gt 4 or 2 lt 3 and 9 gt 10 显然应该评估为 false 但我不太确定 pyth
  • Pandas 中允许重复列

    我将一个大的 CSV 包含股票财务数据 文件分割成更小的块 CSV 文件的格式不同 像 Excel 数据透视表之类的东西 第一列的前几行包含一些标题 公司名称 ID 等在以下列中重复 因为一家公司有多个属性 而不是一家公司只有一栏 在前几行
  • 如何创建一个语句来打印以特定单词开头的单词? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 如何在 python 中打印从特定字母开始的单词 而不使用函数 而是使用方法或循环 1 我有一个字符串 想要打印以 m 开头的单词 S
  • 使用 OLS 回归预测未来值(Python、StatsModels、Pandas)

    我目前正在尝试在 Python 中实现 MLR 但不确定如何将我找到的系数应用于未来值 import pandas as pd import statsmodels formula api as sm import statsmodels
  • 如何从Python中的字符串中提取变量名称和值

    我有一根绳子 data var1 id 12345 name John White python中有没有办法将var1提取为python变量 更具体地说 我对字典变量感兴趣 这样我就可以获得变量的值 id和name python 这是由提供
  • Numpy 过滤器平滑零区域

    我有一个 0 及更大整数的 2D numpy 数组 其中值代表区域标签 例如 array 9 9 9 0 0 0 0 1 1 1 9 9 9 9 0 7 1 1 1 1 9 9 9 9 0 2 2 1 1 1 9 9 9 8 0 2 2 1
  • 将 matplotlib 颜色图集中在特定值上

    我正在使用 matplotlib 颜色图 seismic 绘制绘图 并且希望白色以 0 为中心 当我在不进行任何更改的情况下运行脚本时 白色从 0 下降到 10 我尝试设置 vmin 50 vmax 50 但在这种情况下我完全失去了白色 关
  • 将 JSON 对象传递给带有请求的 url

    所以 我想利用 Kenneth 的优秀请求模块 https github com kennethreitz requests 在尝试使用时偶然发现了这个问题自由库API http wiki freebase com wiki API 基本上
  • Pandas 根据 diff 列形成簇

    我正在尝试使用 Pandas 根据表示时间 以秒为单位 的列中的差异来消除数据框中的一些接近重复项 例如 import pandas as pd numpy as np df pd DataFrame 1200 1201 1233 1555
  • 默认情况下,Keras 自定义层参数是不可训练的吗?

    我在 Keras 中构建了一个简单的自定义层 并惊讶地发现参数默认情况下未设置为可训练 我可以通过显式设置可训练属性来使其工作 我无法通过查看文档或代码来解释为什么会这样 这是应该的样子还是我做错了什么导致默认情况下参数不可训练 代码 im
  • 如何为每个屏幕添加自己的 .py 和 .kv 文件?

    我想为每个屏幕都有一个单独的 py 和 kv 文件 应通过 main py main kv 中的 ScreenManager 选择屏幕 设计应从文件 screen X kv 加载 类等应从文件 screen X py 加载 Screens
  • 字典和数组作为类变量与实例变量

    这是赚取积分的简单方法 请解释以下内容 class C a b 0 c def init self self x def d self k v self x k v self a k v self b v self c append v d
  • 如何读取Python字节码?

    我很难理解 Python 的字节码及其dis module import dis def func x 1 dis dis func 上述代码在解释器中输入时会产生以下输出 0 LOAD CONST 1 1 3 STORE FAST 0 x
  • 从 Twitter API 2.0 获取 user.fields 时出现问题

    我想从 Twitter API 2 0 端点加载推文 并尝试获取标准字段 作者 文本 和一些扩展字段 尤其是 用户 字段 端点和参数的定义工作没有错误 在生成的 json 中 我只找到标准字段 但没有找到所需的 user fields 用户
  • 您可以使用关键字参数而不提供默认值吗?

    我习惯于在 Python 中使用这样的函数 方法定义 def my function arg1 None arg2 default do stuff here 如果我不供应arg1 or arg2 那么默认值None or default
  • Scrapy Spider不存储状态(持久状态)

    您好 有一个基本的蜘蛛 可以运行以获取给定域上的所有链接 我想确保它保持其状态 以便它可以从离开的位置恢复 我已按照给定的网址进行操作http doc scrapy org en latest topics jobs html http d

随机推荐

  • 从 Crystal Report 中的多个表推送数据

    我有一个名为 CR1 的水晶报表 现在我想将多个表中的数据填充到我的水晶报表 CR1 中 我使用的是VS2008 编码语言是ASP net中的C 任何帮助将不胜感激 创建一个存储过程然后将其用作报告的数据源 我会帮你的
  • 无法重现类型擦除示例的结果

    我正在阅读 Java 泛型和集合 第 8 4 节 作者在尝试解释二进制兼容性时定义了以下代码 interface Name extends Comparable public int compareTo Object o class Sim
  • python中列表切片语法的问题

    python 的文档中提到了扩展索引语法 slice start stop step 使用扩展索引语法时也会生成切片对象 例如 a start stop step or a start stop i See itertools islice
  • 在VBA中从SQL Server获取数据

    您好 下面是我的代码 我无法从 SQL 服务器获取数据 它的抛出错误为 Compiler error object required 连接没有问题 连接成功 请更正我的代码 帮我解决这个问题 Private Sub CommandButto
  • 迭代范围的函数式方法(ES6/7)[重复]

    这个问题在这里已经有答案了 以更实用的方式执行以下操作的最佳方法是什么 使用 ES6 ES7 let cols for let i 0 i lt 7 i cols push i i return cols 我尝试过 return 7 map
  • jQuery - 正则表达式选择和removeClass()?

    我收到了几个自动生成的 HTML 文档 长达数千行 我需要清理源代码 大多数情况下需要删除 table col 之类的类名 这是一个两步问题 选择具有 table col 的任何及所有类 其中 是 0 999 之间的整数 从元素中删除匹配的
  • 如何执行 GitHub 上的 hello_world 示例:linuxkit/linuxkit?

    情况与问题 我正在尝试跟随本指南 https medium com notsinge making your own linuxkit with docker for mac 5c1234170fb1关于 如何使用 docker for m
  • C++ boost::graph 从有向图中获取父顶点

    我有一个有向图 通过 boost graph 库中的 adjacency graph 实现 我试图找到某个顶点的父顶点 过去 通过 pygraph 我只是简单地反转了有向图 然后进行了邻居搜索 但似乎使用 boost reverse gra
  • Pandas 忽略 NaN 删除重复项

    在 Pandas df 中 我尝试删除多个列中的重复项 每行有很多数据NaN 这只是一个例子 数据是一个混合包 因此存在许多不同的组合 df drop duplicates IDnum name formNumber 1 NaN AP GR
  • 使用 URL 启动应用程序

    我读过有关 android 中的意图的内容 但这是我的问题 我想通过点击网络浏览器中的链接来启动我的 Android 手机上的应用程序 例子 如果链接是 mycam http camcorder com http camcorder com
  • 如何在JQuery中选择除单击元素之外的所有类?

    我有一个在 Drupal 上开发的网站 我使用一个名为 collapsiblock 的模块 它基本上是一个 JQuery 插件 来实现类似手风琴的效果 它对我来说工作得很好 尽管它还处于测试阶段 但我想修改它 以便当用户单击手风琴的一项时
  • 点击列表视图项目上的手势

    我试图在点击列表视图中的项目后打开另一个视图 我尝试过添加一个TapGestureRegonizer甚至添加ViewCell与网格等 这些似乎都不起作用 我在标签中添加了点击手势 这似乎有效 但对于列表视图项目却不起作用 对于列表视图之类的
  • Rails:按两列之和排序

    所以 我有一个Photo模型可以在以下网址下载full size and presentation size 当用户下载照片时 我会在照片的full downloads and presentation downloads属性 这一切都很好
  • Android:如何从资源文件创建文件对象?

    我的资产文件夹中有一个文本文件 我需要将其转换为文件对象 而不是输入流 当我尝试这个时 我得到 没有这样的文件 异常 String path file android asset datafile txt URL url new URL p
  • Application Insights 遥测筛选不起作用

    我已经按照指南操作了here https learn microsoft com en us azure application insights app insights api filtering sampling filtering
  • Signtool 无法使用时间戳对 SHA2 和 SHA1 进行双重签名

    我们需要使用signtool exe使用SHA1和SHA2对我们的二进制文件进行双重签名 我们的证书支持256位SHA2 使用Windows 8 SDK的signtool e g Signtool exe 签名 as fd sha256 t
  • 如何在 R 中对交集/组索引的结果重新编号?

    我在 R 中对交集 组索引的重新编号结果苦苦挣扎了几天 示例数据框如下所示 t lt data frame mid c 102 102 102 102 102 102 102 103 103 103 103 103 103 103 aid
  • __init__() 内部和外部变量之间的差异(类和实例属性)

    除了名称之外 这些类之间还有什么区别吗 class WithClass def init self self value Bob def my func self print self value class WithoutClass va
  • Delphi 属性真实世界示例在哪里?

    我知道通过TMS奥勒留 http www tmssoftware com site aurelius asp例如 我们可以使用 新 2010 属性功能在运行时将数据库表字段序列化为对象属性 而我不是这种深层面向对象模式的专家 因此我研究了
  • 只要有待处理的取消屏蔽任务留下但不再存在,我如何运行 asyncio 循环?

    我正在尝试向现有的 asyncio 循环添加一些代码 以提供 Ctrl C 上的干净关闭 下面是它正在做的事情的抽象 import asyncio signal async def task1 print Starting simulate