在异步环境中实现回调:深入理解与技巧

115 阅读3分钟
# 在异步环境中实现回调:深入理解与技巧

## 引言

在现代编程中,尤其是处理复杂任务的并发执行时,异步编程变得越来越重要。回调(Callback)是异步编程中常用的模式。本篇文章旨在引导您理解如何在异步环境中使用和实现自定义的回调处理程序(Callback Handlers),并提供实用的代码示例。

## 主要内容

### 什么是回调?

回调是指在某个函数执行完毕后,自动调用的函数。这一机制在异步编程中尤为重要,因为它允许执行异步任务后触发特定行为,而无需阻塞主程序。

### 异步回调处理程序

在异步环境中,为了避免阻塞事件循环,我们更倾向于使用 `AsyncCallbackHandler`。通过扩展这一类,我们可以实现自定义的异步回调逻辑。

### 同步与异步的混合使用

注意在异步方法中使用同步回调处理程序(`syncCallbackHandler`),虽然可行,但会在后台通过`run_in_executor`运行,这可能导致线程安全问题。因此,在异步环境中,优先使用异步回调处理程序。

### 版本兼容性注意事项

在 Python 版本小于等于3.10的情况下,调用其他可运行对象时必须正确传播配置(config)或回调,否则这些回调将不会传递给被调用对象的子运行任务。

## 代码示例

以下代码展示了如何使用自定义的同步和异步回调处理程序与 `langchain` 结合:

```python
import asyncio
from typing import Any, Dict, List
from langchain_anthropic import ChatAnthropic
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()],
)

await chat.agenerate([[HumanMessage(content="Tell me a joke")]])

常见问题和解决方案

  • 线程安全问题:确保异步回调处理程序中共享资源的访问是线程安全的。
  • 回调未触发:检查回调的注册及其传递链,确保在多个可运行对象之间正确传播。
  • 版本兼容性:使用 Python <= 3.10 时,需要留意配置及回调的传播。

总结和进一步学习资源

回调在异步编程中扮演重要角色,通过学习如何在异步环境中实现和使用回调,您可以创建更高效和响应更快的应用程序。建议阅读更多关于异步编程的资源,以深入了解这一领域的最佳实践。

参考资料

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!

---END---