[python]同步调用和异步调用

184 阅读6分钟

1/Python中的同步调用与异步调用

<1>同步调用 (Synchronous Calls)

同步调用是指代码按顺序执行,每个操作必须等待前一个操作完成后才能开始。这是默认的程序执行方式。

同步调用的特点:

  • 顺序执行,易于理解和调试
  • 阻塞式操作,当前操作完成前不能执行其他代码
  • 适合简单、线性的任务

同步实现方式:

  • 普通函数调用
  • 直接执行代码

<2>异步调用 (Asynchronous Calls)

异步调用是指代码可以在等待某个操作完成时继续执行其他任务,而不是阻塞等待。

异步调用的特点:

  • 非阻塞,提高程序效率
  • 适合I/O密集型任务(网络请求、文件读写等)
  • 代码结构更复杂,需要事件循环

异步实现方式:

  • asyncio 库 (Python 3.5+)
  • async/await 关键字
  • 回调函数
  • 线程/进程池

2/完整示例

示例1:同步调用示例

import time

def sync_task(name, delay):
    """模拟一个耗时的同步任务"""
    print(f"任务 {name} 开始执行,需要 {delay} 秒")
    time.sleep(delay)  # 模拟耗时操作
    print(f"任务 {name} 完成")
    return f"任务 {name} 结果"

def synchronous_example():
    """同步调用示例"""
    print("=== 同步调用示例 ===")
    start_time = time.time()
    
    # 顺序执行任务
    result1 = sync_task("A", 2)
    result2 = sync_task("B", 1)
    result3 = sync_task("C", 3)
    
    end_time = time.time()
    print(f"\n所有任务完成!")
    print(f"结果: {result1}, {result2}, {result3}")
    print(f"总耗时: {end_time - start_time:.2f} 秒")

if __name__ == "__main__":
    synchronous_example()

示例2:使用asyncio的异步调用

import asyncio
import time

async def async_task(name, delay):
    """模拟一个耗时的异步任务"""
    print(f"异步任务 {name} 开始执行,需要 {delay} 秒")
    await asyncio.sleep(delay)  # 异步等待,不会阻塞事件循环
    print(f"异步任务 {name} 完成")
    return f"异步任务 {name} 结果"

async def asynchronous_example():
    """异步调用示例"""
    print("=== 异步调用示例 ===")
    start_time = time.time()
    
    # 创建任务但不立即等待
    task1 = asyncio.create_task(async_task("A", 2))
    task2 = asyncio.create_task(async_task("B", 1))
    task3 = asyncio.create_task(async_task("C", 3))
    
    # 等待所有任务完成
    results = await asyncio.gather(task1, task2, task3)
    
    end_time = time.time()
    print(f"\n所有异步任务完成!")
    print(f"结果: {results}")
    print(f"总耗时: {end_time - start_time:.2f} 秒")

async def asynchronous_with_timeout():
    """带有超时控制的异步调用示例"""
    print("\n=== 异步调用(带超时)示例 ===")
    
    try:
        # 设置任务超时时间
        result = await asyncio.wait_for(async_task("D", 5), timeout=3.0)
        print(f"任务完成: {result}")
    except asyncio.TimeoutError:
        print("任务超时!")

def run_async_examples():
    """运行异步示例"""
    # 运行基本异步示例
    asyncio.run(asynchronous_example())
    
    # 运行带超时的异步示例
    asyncio.run(asynchronous_with_timeout())

if __name__ == "__main__":
    run_async_examples()

示例3:使用线程池的异步调用

import concurrent.futures
import time
import random

def cpu_intensive_task(name, iterations):
    """模拟一个CPU密集型任务"""
    print(f"CPU任务 {name} 开始执行")
    result = 0
    for i in range(iterations):
        result += i * random.random()
    print(f"CPU任务 {name} 完成")
    return f"CPU任务 {name} 结果: {result}"

def io_intensive_task(name, delay):
    """模拟一个I/O密集型任务"""
    print(f"IO任务 {name} 开始执行,需要 {delay} 秒")
    time.sleep(delay)  # 模拟I/O等待
    print(f"IO任务 {name} 完成")
    return f"IO任务 {name} 结果"

def thread_pool_example():
    """使用线程池的异步调用示例"""
    print("=== 线程池异步调用示例 ===")
    start_time = time.time()
    
    # 使用线程池执行I/O密集型任务
    with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
        # 提交任务到线程池
        future1 = executor.submit(io_intensive_task, "A", 2)
        future2 = executor.submit(io_intensive_task, "B", 1)
        future3 = executor.submit(io_intensive_task, "C", 3)
        
        # 获取结果
        results = []
        for future in concurrent.futures.as_completed([future1, future2, future3]):
            result = future.result()
            results.append(result)
            print(f"获取到结果: {result}")
    
    end_time = time.time()
    print(f"\n所有线程池任务完成!")
    print(f"总耗时: {end_time - start_time:.2f} 秒")

def process_pool_example():
    """使用进程池的异步调用示例(适合CPU密集型任务)"""
    print("\n=== 进程池异步调用示例 ===")
    start_time = time.time()
    
    # 使用进程池执行CPU密集型任务
    with concurrent.futures.ProcessPoolExecutor(max_workers=3) as executor:
        # 提交任务到进程池
        futures = [
            executor.submit(cpu_intensive_task, f"任务{i}", 10000000)
            for i in range(1, 5)
        ]
        
        # 获取结果
        for future in concurrent.futures.as_completed(futures):
            result = future.result()
            print(f"获取到结果: {result}")
    
    end_time = time.time()
    print(f"\n所有进程池任务完成!")
    print(f"总耗时: {end_time - start_time:.2f} 秒")

if __name__ == "__main__":
    thread_pool_example()
    process_pool_example()

示例4:混合使用同步和异步

import asyncio
import time
import aiohttp
import requests

# 同步网络请求函数
def sync_fetch_url(url):
    """同步获取网页内容"""
    print(f"同步请求: {url}")
    response = requests.get(url, timeout=5)
    print(f"同步请求完成: {url}, 状态码: {response.status_code}")
    return response.status_code

# 异步网络请求函数
async def async_fetch_url(session, url):
    """异步获取网页内容"""
    print(f"异步请求: {url}")
    async with session.get(url, timeout=5) as response:
        print(f"异步请求完成: {url}, 状态码: {response.status}")
        return response.status

async def mixed_example():
    """混合使用同步和异步的示例"""
    print("=== 混合同步/异步示例 ===")
    start_time = time.time()
    
    # 同步请求
    print("\n1. 执行同步请求...")
    sync_start = time.time()
    sync_result = sync_fetch_url("https://httpbin.org/get")
    print(f"同步请求耗时: {time.time() - sync_start:.2f} 秒")
    
    # 异步请求
    print("\n2. 执行异步请求...")
    async_start = time.time()
    
    # 创建多个异步任务
    urls = [
        "https://httpbin.org/delay/1",
        "https://httpbin.org/delay/2",
        "https://httpbin.org/status/200",
        "https://httpbin.org/status/404"
    ]
    
    async with aiohttp.ClientSession() as session:
        tasks = [async_fetch_url(session, url) for url in urls]
        async_results = await asyncio.gather(*tasks, return_exceptions=True)
    
    print(f"异步请求耗时: {time.time() - async_start:.2f} 秒")
    
    end_time = time.time()
    print(f"\n所有任务完成!")
    print(f"同步结果: {sync_result}")
    print(f"异步结果: {async_results}")
    print(f"总耗时: {end_time - start_time:.2f} 秒")

def run_mixed_example():
    """运行混合示例"""
    asyncio.run(mixed_example())

if __name__ == "__main__":
    run_mixed_example()

示例5:回调函数方式的异步调用

import threading
import time
import queue

def long_running_task(task_id, duration, callback):
    """模拟长时间运行的任务,完成后调用回调函数"""
    def task():
        print(f"任务 {task_id} 开始执行,需要 {duration} 秒")
        time.sleep(duration)
        result = f"任务 {task_id} 完成,耗时 {duration} 秒"
        # 执行回调函数
        callback(result)
    
    # 在新线程中执行任务
    thread = threading.Thread(target=task)
    thread.start()
    return thread

def callback_example():
    """使用回调函数的异步调用示例"""
    print("=== 回调函数异步调用示例 ===")
    start_time = time.time()
    
    # 创建队列用于收集结果
    result_queue = queue.Queue()
    
    # 定义回调函数
    def task_callback(result):
        print(f"回调函数收到: {result}")
        result_queue.put(result)
    
    # 启动多个异步任务
    threads = []
    for i, duration in enumerate([2, 1, 3], 1):
        thread = long_running_task(i, duration, task_callback)
        threads.append(thread)
    
    # 等待所有线程完成
    for thread in threads:
        thread.join()
    
    # 收集结果
    results = []
    while not result_queue.empty():
        results.append(result_queue.get())
    
    end_time = time.time()
    print(f"\n所有回调任务完成!")
    print(f"结果: {results}")
    print(f"总耗时: {end_time - start_time:.2f} 秒")

if __name__ == "__main__":
    callback_example()

总结对比

特性同步调用异步调用
执行方式顺序执行,阻塞非阻塞,可并发
代码复杂度简单直观相对复杂
适用场景简单任务、CPU密集型I/O密集型、高并发
性能较低(等待时间长)较高(资源利用率高)
实现方式普通函数调用asyncio、线程池、进程池、回调

选择建议

  1. 使用同步调用当:

    • 任务简单,不需要并发
    • 代码可读性和简单性是首要考虑
    • 任务之间有明显依赖关系
  2. 使用异步调用当:

    • 有大量I/O操作(网络请求、文件读写)
    • 需要高并发处理
    • 程序性能是关键考虑因素
  3. 选择异步实现方式

    • asyncio:Python原生支持,适合I/O密集型任务
    • 线程池:适合I/O密集型任务,简单易用
    • 进程池:适合CPU密集型任务,避免GIL限制
    • 回调函数:传统异步模式,控制灵活但代码复杂

这些示例展示了Python中同步和异步调用的不同实现方式,你可以根据具体需求选择合适的方法。