本文目录导读:

在Python中过滤协程重复请求任务,我提供几种常用且有效的方案:
基于集合去重(简单任务)
import asyncio
from typing import Any, Callable
class RequestDeduplicator:
def __init__(self):
self._running_tasks = set()
self._lock = asyncio.Lock()
async def execute_unique(self, task_id: str, coro_factory: Callable[[], Any]) -> Any:
"""执行唯一任务,防止重复"""
async with self._lock:
if task_id in self._running_tasks:
raise ValueError(f"任务 '{task_id}' 已在执行中")
self._running_tasks.add(task_id)
try:
coro = coro_factory()
result = await coro
return result
finally:
async with self._lock:
self._running_tasks.discard(task_id)
# 使用示例
async def fetch_data(url: str) -> str:
await asyncio.sleep(1) # 模拟网络请求
return f"数据来自 {url}"
async def main():
deduplicator = RequestDeduplicator()
url = "https://example.com/api"
# 重复请求会抛出异常
task1 = asyncio.create_task(
deduplicator.execute_unique(url, lambda: fetch_data(url))
)
task2 = asyncio.create_task(
deduplicator.execute_unique(url, lambda: fetch_data(url))
)
try:
results = await asyncio.gather(task1, task2, return_exceptions=True)
print(results)
except ValueError as e:
print(f"重复请求被拦截: {e}")
缓存结果去重(智能缓存)
import asyncio
from functools import wraps
from typing import Dict, Any, Optional
import time
class AsyncCacheDeduplicator:
def __init__(self, ttl: int = 60):
self._cache: Dict[str, tuple] = {} # task_id -> (result, timestamp)
self._locks: Dict[str, asyncio.Lock] = {}
self._ttl = ttl
async def get_or_execute(self, task_id: str, coro_factory) -> Any:
"""获取缓存结果或执行新任务"""
# 检查缓存
if task_id in self._cache:
result, timestamp = self._cache[task_id]
if time.time() - timestamp < self._ttl:
return result
# 确保每个任务唯一锁
if task_id not in self._locks:
self._locks[task_id] = asyncio.Lock()
lock = self._locks[task_id]
async with lock:
# 二次检查(另一个协程可能已经完成)
if task_id in self._cache:
result, timestamp = self._cache[task_id]
if time.time() - timestamp < self._ttl:
return result
# 执行任务
coro = coro_factory()
result = await coro
# 缓存结果
self._cache[task_id] = (result, time.time())
return result
def invalidate(self, task_id: str):
"""清除缓存"""
self._cache.pop(task_id, None)
# 使用示例
cache_dedup = AsyncCacheDeduplicator(ttl=30)
async def expensive_operation(task_id: str) -> str:
await asyncio.sleep(2)
return f"计算结果 {task_id}"
async def main():
# 多个协程请求相同任务,只执行一次
tasks = [
cache_dedup.get_or_execute("calc_1", lambda: expensive_operation("calc_1"))
for _ in range(5)
]
results = await asyncio.gather(*tasks)
print(f"结果: {results}")
使用异步信号量控制并发
import asyncio
from typing import Dict, Set
class RateLimitedDeduplicator:
def __init__(self, max_concurrent: int = 10):
self._semaphore = asyncio.Semaphore(max_concurrent)
self._pending: Dict[str, asyncio.Future] = {}
self._lock = asyncio.Lock()
async def execute(self, task_id: str, coro_factory) -> Any:
"""带并发限制的重复请求过滤"""
async with self._lock:
# 如果任务已在执行,共享同一个Future
if task_id in self._pending:
return await self._pending[task_id]
# 创建新Future
future = asyncio.get_event_loop().create_future()
self._pending[task_id] = future
try:
async with self._semaphore:
coro = coro_factory()
result = await coro
future.set_result(result)
except Exception as e:
future.set_exception(e)
finally:
async with self._lock:
self._pending.pop(task_id, None)
return await future
# 使用示例
limiter = RateLimitedDeduplicator(max_concurrent=5)
async def process_request(task_id: str) -> str:
await asyncio.sleep(1)
return f"任务 {task_id} 完成"
async def main():
# 模拟多个重复请求
requests = [
("task_1", lambda: process_request("task_1")),
("task_1", lambda: process_request("task_1")), # 重复
("task_2", lambda: process_request("task_2")),
("task_2", lambda: process_request("task_2")), # 重复
]
tasks = [
limiter.execute(task_id, coro_factory)
for task_id, coro_factory in requests
]
results = await asyncio.gather(*tasks, return_exceptions=True)
for i, result in enumerate(results):
print(f"请求 {i}: {result}")
通用装饰器方案
import asyncio
from functools import wraps
from typing import Dict, Any, Callable
def dedup_async(ttl: int = 60):
"""异步函数去重装饰器"""
def decorator(func: Callable) -> Callable:
cache: Dict[str, asyncio.Future] = {}
lock = asyncio.Lock()
@wraps(func)
async def wrapper(*args, **kwargs):
# 生成任务标识
task_id = f"{func.__name__}:{args}:{kwargs}"
async with lock:
# 如果有正在执行的任务,复用结果
if task_id in cache and not cache[task_id].done():
return await cache[task_id]
# 创建新Future
future = asyncio.get_event_loop().create_future()
cache[task_id] = future
try:
result = await func(*args, **kwargs)
future.set_result(result)
return result
except Exception as e:
future.set_exception(e)
raise e
finally:
# 延迟清理
await asyncio.sleep(ttl)
async with lock:
cache.pop(task_id, None)
return wrapper
return decorator
# 使用示例
@dedup_async(ttl=10)
async def fetch_user_data(user_id: int) -> dict:
await asyncio.sleep(2)
return {"user_id": user_id, "name": f"用户{user_id}"}
async def main():
# 多个协程同时请求同一个用户数据
tasks = [
fetch_user_data(1)
for _ in range(3)
]
results = await asyncio.gather(*tasks)
print(f"所有结果相同: {all(r == results[0] for r in results)}")
选择建议
- 简单去重:使用集合方案(方案1)
- 需要缓存结果:使用缓存方案(方案2)
- 控制并发:使用信号量方案(方案3)
- 装饰器风格:使用装饰器方案(方案4)
根据你的具体场景选择合适的方案,如果需要更复杂的逻辑(如Redis分布式去重),可以基于这些基础方案扩展。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播