首页 > 其他分享 >tornado——关于tornado的异步操作学习

tornado——关于tornado的异步操作学习

时间:2023-10-27 15:24:54浏览次数:35  
标签:__ 异步 tornado await 学习 main asyncio

关于tornado的异步操作学习

yield http_client.fetch和yield tornado.gen.Task(http_client.fetch的区别

实际上,yield http_client.fetchyield tornado.gen.Task(http_client.fetch) 是等价的,它们在功能上是相同的。tornado.gen.Task 是 Tornado 4.0 版本之前的写法,而 yield http_client.fetch 是在 Tornado 4.0 版本之后引入的新语法。

Tornado 4.0 版本引入了新的生成器语法,使得使用 yield 关键字来暂停协程并等待异步操作的完成更加直观和简洁。因此,推荐使用 yield http_client.fetch 这种新的语法来编写代码。

以下是使用新语法的示例:

import tornado.gen
import tornado.httpclient
import tornado.ioloop

@tornado.gen.coroutine
def send_async_request(url, method, body=None):
    http_client = tornado.httpclient.AsyncHTTPClient()
    response = yield http_client.fetch(url, method=method, body=body)
    # 在这里可以对响应进行处理
    # 进行其他操作...

def main():
    tornado.ioloop.IOLoop.current().run_sync(lambda: send_async_request("http://example.com", "GET"))

if __name__ == "__main__":
    main()

使用新语法的代码更加简洁和易读,因此推荐使用 yield http_client.fetch 这种写法。

yield http_client.fetch,是表示要开启协程异步操作,那和await的区别是什么

yield http_client.fetch 表示在协程中暂停执行,并发起一个异步操作。而 await 关键字则用于等待异步操作的完成,并获取其结果。

在 Tornado 中,await 关键字通常与 async def 结合使用,用于定义异步函数。在异步函数中,可以使用 await 关键字等待异步操作的完成,并将结果返回。

以下是一个使用 await 的示例:

import tornado.httpclient
import tornado.ioloop

async def send_async_request(url, method, body=None):
    http_client = tornado.httpclient.AsyncHTTPClient()
    response = await http_client.fetch(url, method=method, body=body)
    # 在这里可以对响应进行处理
    # 进行其他操作...

def main():
    tornado.ioloop.IOLoop.current().run_sync(lambda: send_async_request("http://example.com", "GET"))

if __name__ == "__main__":
    main()

在上述示例中,send_async_request 函数使用 async def 声明为异步函数。通过 await http_client.fetch(url, method=method, body=body),函数会在异步请求完成后暂停,并将结果赋值给 response 变量。

使用 await 关键字可以使代码更加清晰和易读,特别是在处理多个异步操作时,可以使用 await 来等待每个异步操作的完成,以便按顺序执行后续的逻辑。

总结起来,yieldawait 都可以用于在协程中等待异步操作的完成,不同的是语法和使用方式略有不同。对于 Tornado 4.0 版本及以上的代码,推荐使用 await 关键字来等待异步操作的完成。

比如我await的方法,是一个遛狗的代码,要如何实现

如果你想编写一个使用 await 的遛狗的代码示例,可以考虑使用 Tornado 的异步框架来模拟一个遛狗的过程。这里是一个简单的示例:

import asyncio
import time

async def walk_dog():
    print(f"[{time.strftime('%H:%M:%S')}] 开始遛狗")
    await asyncio.sleep(2)  # 模拟遛狗过程,等待2秒
    print(f"[{time.strftime('%H:%M:%S')}] 遛狗结束")

async def main():
    print(f"[{time.strftime('%H:%M:%S')}] 准备遛狗")
    await walk_dog()
    print(f"[{time.strftime('%H:%M:%S')}] 回家")

if __name__ == "__main__":
    asyncio.run(main())

在上述示例中,walk_dog 函数被定义为一个异步函数,使用 await asyncio.sleep(2) 来模拟遛狗的过程,等待2秒。在 main 函数中,我们调用 await walk_dog() 来等待遛狗操作的完成。

当我们运行这个示例时,输出将按照以下顺序显示:

[14:55:05] 准备遛狗
[14:55:05] 开始遛狗
[14:55:07] 遛狗结束
[14:55:07] 回家

这个示例展示了如何使用 await 在协程中等待异步操作的完成。在实际的代码中,你可以将遛狗的过程替换为任何其他的异步操作,例如发送网络请求、读取文件等。

所以如果有超多的狗要遛,可以同时执行,最终最后一只狗遛完了才会return

是的,使用异步操作可以实现同时遛多只狗的效果,并在最后一只狗遛完后返回结果。你可以使用 asyncio.gather 函数来同时执行多个异步任务,并等待它们全部完成。

以下是一个示例代码:

import asyncio
import time

async def walk_dog(dog_name):
    print(f"[{time.strftime('%H:%M:%S')}] 开始遛狗 {dog_name}")
    await asyncio.sleep(2)  # 模拟遛狗过程,等待2秒
    print(f"[{time.strftime('%H:%M:%S')}] {dog_name} 遛狗结束")

async def main():
    dog_names = ["旺财", "小白", "大黄"]  # 假设有三只狗需要遛
    tasks = [walk_dog(dog_name) for dog_name in dog_names]
    await asyncio.gather(*tasks)
    print(f"[{time.strftime('%H:%M:%S')}] 所有狗都遛完了,回家")

if __name__ == "__main__":
    asyncio.run(main())

在这个示例中,我们定义了一个 walk_dog 函数来遛狗,接受一个狗的名字作为参数。main 函数中创建了多个遛狗任务,并使用 asyncio.gather 函数将这些任务组合起来。然后使用 await 等待它们全部完成。

运行这个示例,输出将类似于以下内容:

[14:55:05] 开始遛狗 旺财
[14:55:05] 开始遛狗 小白
[14:55:05] 开始遛狗 大黄
[14:55:07] 旺财 遛狗结束
[14:55:07] 小白 遛狗结束
[14:55:07] 大黄 遛狗结束
[14:55:07] 所有狗都遛完了,回家

你可以看到,三只狗同时开始遛,等待2秒后依次完成,然后输出 "所有狗都遛完了,回家"。

使用异步操作可以实现高效的并发处理,提高程序的执行效率。希望这个示例对你有帮助。如果还有其他问题,请随时提问。

这样操作数据的时候,会有数据混乱的情况吗

在异步操作中,如果多个任务同时访问和修改共享的数据,确实存在数据混乱的情况。这种情况被称为竞态条件(Race Condition)。

当多个任务同时读取和写入共享数据时,由于它们的执行顺序是不确定的,可能会导致数据不一致或意外的结果。这是因为任务之间的执行是并发的,彼此之间相互干扰。

为了避免数据混乱的情况,可以采取以下措施之一:

  1. 使用互斥锁(Mutex):在访问共享数据之前,使用互斥锁来确保同一时间只有一个任务可以访问数据。这样可以避免并发访问导致的问题。在Python中,可以使用asyncio.Lock来实现互斥锁。

  2. 使用线程安全的数据结构:如果你使用的是线程安全的数据结构,例如asyncio.Queue,它们内部已经实现了对共享数据的安全访问。

  3. 避免共享状态:尽量设计任务之间不共享数据的方式,而是通过消息传递或其他方式进行通信。这样可以避免竞态条件的出现。

具体采取哪种方式取决于你的具体需求和场景。在实际开发中,需要根据情况选择合适的方法来保证数据的一致性和正确性。

当涉及到共享数据时,以下是几个可能导致数据混乱的常见情况的示例:

  1. 并发写入数据:
import asyncio

shared_data = []

async def write_data(value):
    shared_data.append(value)

async def main():
    tasks = [write_data(i) for i in range(5)]
    await asyncio.gather(*tasks)
    print(shared_data)

if __name__ == "__main__":
    asyncio.run(main())

在这个示例中,多个任务同时向 shared_data 列表中写入数据。由于多个任务并发执行,可能会导致数据混乱。输出结果可能是不确定的,例如 [0, 2, 1, 4, 3]

解决方法之一是使用互斥锁来保护共享数据:

import asyncio

shared_data = []
lock = asyncio.Lock()

async def write_data(value):
    async with lock:
        shared_data.append(value)

async def main():
    tasks = [write_data(i) for i in range(5)]
    await asyncio.gather(*tasks)
    print(shared_data)

if __name__ == "__main__":
    asyncio.run(main())

在这个示例中,我们使用了 asyncio.Lock 来创建一个互斥锁 lock,并在写入共享数据之前使用 async with lock 来确保同一时间只有一个任务可以修改数据。这样可以避免数据混乱。

  1. 并发读取和写入数据:
import asyncio

shared_data = 0

async def increment():
    global shared_data
    shared_data += 1

async def decrement():
    global shared_data
    shared_data -= 1

async def main():
    tasks = [increment(), decrement()]
    await asyncio.gather(*tasks)
    print(shared_data)

if __name__ == "__main__":
    asyncio.run(main())

在这个示例中,两个任务同时读取和修改 shared_data 的值。由于并发执行,可能会导致数据混乱。输出结果可能是不确定的,例如 01-1

解决方法之一是使用原子操作或线程安全的数据结构,例如 asyncio.AtomicInt

import asyncio

shared_data = asyncio.AtomicInt(0)

async def increment():
    shared_data.add(1)

async def decrement():
    shared_data.sub(1)

async def main():
    tasks = [increment(), decrement()]
    await asyncio.gather(*tasks)
    print(shared_data)

if __name__ == "__main__":
    asyncio.run(main())

在这个示例中,我们使用了 asyncio.AtomicInt 来创建一个原子整数 shared_data,它提供了原子操作来确保并发修改的安全性。这样可以避免数据混乱。

这些示例展示了一些可能导致数据混乱的情况以及如何解决它们。根据具体的需求和场景,可能需要采取不同的方法来保证数据的一致性和正确性。

基于装饰器@tornado.gen.coroutine使用yield http_client.fetch和await http_client.fetch有啥区别

在 Tornado 框架中,@tornado.gen.coroutine 装饰器和 yield http_client.fetch 以及 await http_client.fetch 之间有一些区别。

  1. 使用 @tornado.gen.coroutine 装饰器和 yield
import tornado.gen
from tornado.httpclient import AsyncHTTPClient

@tornado.gen.coroutine
def fetch_urls(urls):
    http_client = AsyncHTTPClient()
    for url in urls:
        response = yield http_client.fetch(url)
        # 处理响应数据
        print(response.body)

在这个示例中,@tornado.gen.coroutine 装饰器用于将函数标记为生成器协程。使用 yield 关键字可以将函数的执行暂停,并返回一个 Future 对象,该对象表示异步操作的结果。在这种情况下,http_client.fetch(url) 返回的是一个 Future 对象,通过 yield 关键字将其暂停函数的执行,直到异步操作完成并返回结果。

  1. 使用 await
import tornado.gen
from tornado.httpclient import AsyncHTTPClient

async def fetch_urls(urls):
    http_client = AsyncHTTPClient()
    for url in urls:
        response = await http_client.fetch(url)
        # 处理响应数据
        print(response.body)

在这个示例中,async 关键字用于定义一个异步函数(或协程)。使用 await 关键字可以等待异步操作的结果。在这种情况下,http_client.fetch(url) 返回一个 Future 对象,通过 await 关键字等待该 Future 对象的结果,暂停函数的执行,直到异步操作完成并返回结果。

总的来说,yieldawait 都可以用于暂停函数的执行,等待异步操作的完成。但是使用 await 更加直观和简洁,是 Python 3.5+ 中引入的 asyncio 语法的一部分。而使用 yield 需要结合 @tornado.gen.coroutine 装饰器来标记函数为生成器协程,并且需要使用 tornado.gen 模块提供的工具函数来处理 Future 对象。在新的代码中,推荐使用 await 语法来处理异步操作。

标签:__,异步,tornado,await,学习,main,asyncio
From: https://www.cnblogs.com/pywjh/p/17792420.html

相关文章

  • 易语言银行余额虚拟生成器制作,提供源码思路,仅供学习
    今天这边带来的是一个图片生成器,是用易语言进行开发的,整个代码我算了一下不超过10行,然后就需要一个图片框组件和三个编辑框,三个标签,一个按钮就能实现,真的非常非常简单,大家可以照猫画虎哈,这也仅仅只是为大家做的一个演示示范。软件截图:程序集源码分享:.版本2.程序集窗口程......
  • MySQL学习(10)基于规则的优化
    前言MySQL为了更高的执行效率,会将客户端发送的SQL语句进行优化。条件化简MySQL优化器会对SQL语句中的表达式进行简化处理,以提高执行效率。移除不必要的括号。常量传递。a=5ANDb>a可优化为a=5ANDb>5。移除没用的条件。优化器会移除掉明显为TRUE或FALSE的表......
  • 易语言开发银行流水生成器,分享源码仅供学习
    小编我是做技术开发的,易语言的话学了将近有10年左右,然后看网上很多人都在找这个玩意,然后就是有一个人卖9000多,感觉那种人就是向前想疯了,我分析了一下它的软件源码,也就是辅助生成数据,并不是一键它会生成文档,就是给你按参数生成固定的数据,然后需要你自己复制粘贴,这个原理的实现真的......
  • 学习过的网站记录
      https://www.jianshu.com/p/ba9d45b4e592?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes  IOS-地图之CLLocation的使用   https://blog.csdn.net/songzhuo1991/article/details/115626992 iOS-UIStackView布局详解   http://devce......
  • python+playwright 学习-83 page.expect_response()捕获网络返回数据
    前言expect_response()方法可以捕获接口返回的数据,在爬取网页数据时非常有用。expect_response()使用官方文档示例withpage.expect_response("https://example.com/resource")asresponse_info:page.get_by_text("triggerresponse").click()response=response_info.val......
  • python+playwright 学习-82 Request 对象
    前言每当页面发送网络资源请求时,页面都会发出以下事件序列:page.on("request")当页面发出请求时触发page.on("response")接收到请求的响应状态和标头时触发page.on("requestfinished")当响应主体被下载并且请求完成时发出。如果请求在某个时刻失败,则会发出page.on("requestfa......
  • python+playwright 学习-81 page.expect_request()捕获网络请求
    前言page.expect_request()可以捕获网页上发出去的请求,当有多个请求时,可以根据请求url,请求方式判断。expect_request官方文档示例withpage.expect_request("http://example.com/resource")asfirst:page.get_by_text("triggerrequest").click()first_request=first.val......
  • FastAPI学习-21.response 参数-设置响应Cookies
    前言可以在 路径函数 中定义一个类型为 Response的参数,这样你就可以在这个临时响应对象中设置cookie了。response参数设置cookiesfromfastapiimportFastAPI,Responseapp=FastAPI()@app.post("/cookie-and-object/")defcreate_cookie(response:Response):res......
  • FastAPI学习-20.response 参数-设置响应头部
    前言你可以在你的_路径操作函数_中声明一个Response类型的参数。设置响应头部你可以在这个_临时_响应对象中设置头部fromfastapiimportFastAPI,Responseapp=FastAPI()@app.get("/headers-and-object/")defget_headers(response:Response):response.headers["X-C......
  • FastAPI学习-19.response 参数-修改状态码
    前言假设你想默认返回一个HTTP状态码为“OK”200。但如果数据不存在,你想创建它,并返回一个HTTP状态码为“CREATED”201。但你仍然希望能够使用response_model过滤和转换你返回的数据。对于这些情况,你可以使用一个response`参数。使用 response 参数status_code设置默认状态码fr......