用举例来学习Python中的并行性、并发性和异步性

Parallelism, Concurrency, and AsyncIO in Python - by example

用举例来学习Python中的并行性、并发性和异步性

一篇中文译文,来自:(Parallelism, Concurrency, and AsyncIO in Python - by example | TestDriven.io) Posted by Amal Shaji

This tutorial looks at how to speed up CPU-bound and IO-bound operations with multiprocessing, threading, and AsyncIO. 本教程介绍如何通过多进程、线程和 AsyncIO 来加速 CPU 密集型和 IO 密集型操作。

Concurrency vs Parallelism 并发与并行

Concurrency and parallelism are similar terms, but they are not the same thing. 并发和并行是相似的术语,但它们不是同一件事。

Concurrency is the ability to run multiple tasks on the CPU at the same time. Tasks can start, run, and complete in overlapping time periods. In the case of a single CPU, multiple tasks are run with the help of context switching, where the state of a process is stored so that it can be called and executed later. 并发性是指在 CPU 上同时运行多个任务的能力。任务可以在重叠的时间段内启动、运行和完成。在单CPU的情况下,多个任务在上下文切换的帮助下运行,其中(上下文中)存储了进程的状态,以便稍后调用和执行。

Parallelism, meanwhile, is the ability to run multiple tasks at the same time across multiple CPU cores. 与此同时,并行(Parallelism)是指在多个 CPU 核心上同时运行多个任务(multiple tasks)的能力。

Though they can increase the speed of your application, concurrency and parallelism should not be used everywhere. The use case depends on whether the task is CPU-bound or IO-bound. 尽管它们可以提高应用程序的速度,但并发(concurrency)和并行(Parallelism)不应该在所有的地方使用。是否使用取决于任务是 CPU 密集型(CPU-bound)还是 IO 密集型(IO-bound)。

Tasks that are limited by the CPU are CPU-bound. For example, mathematical computations are CPU-bound since computational power increases as the number of computer processors increases. Parallelism is for CPU-bound tasks. 受 CPU 限制的任务是 CPU 密集型的。例如,数学计算受 CPU 限制,因为计算能力随着计算机处理器数量的增加而增加。并行(Parallelism)适用于 CPU 密集型任务。

In theory, If a task is divided into n-subtasks, each of these n-tasks can run in parallel to effectively reduce the time to 1/n of the original non-parallel task. Concurrency is preferred for IO-bound tasks, as you can do something else while the IO resources are being fetched. 理论上,如果一个任务被划分为n个子任务,那么这n个子任务中的每一个都可以并行运行,从而有效地将运行时间减少到原来非并行运行时的1/n。对于 IO 密集型(IO-bound)任务来说,并发(Concurrency)是首选,因为您可以在 IO 资源正在被获取的同时去做其他操作。

The best example of CPU-bound tasks is in data science. Data scientists deal with huge chunks of data. For data preprocessing, they can split the data into multiple batches and run them in parallel, effectively decreasing the total time to process.Increasing the number of cores results in faster processing. CPU 密集型(CPU-bound)任务的最佳示例是数据科学。数据科学家处理大量数据。对于数据预处理,他们可以将数据分成多个批次并行(parallel)运行,从而有效减少总处理时间。增加核心数量可以加快处理速度。

Web scraping is IO-bound. Because the task has little effect on the CPU since most of the time is spent on reading from and writing to the network. Other common IO-bound tasks include database calls and reading and writing files to disk.Web applications, like Django and Flask, are IO-bound applications. Web 抓取是 IO 密集型(IO-bound)。因为该任务对 CPU 的影响很小,因为大部分时间都花在网络读写上。其他常见的 IO 密集型任务包括数据库调用以及向磁盘读取和写入文件。Web 应用程序(例如 Django 和 Flask)是 IO 密集型应用程序。

If you’re interested in learning more about the differences between threads, multiprocessing, and async in Python, check out the Speeding Up Python with Concurrency, Parallelism, and asyncio article. 如果您有兴趣了解有关 Python 中线程、多处理和异步之间差异的更多信息,请查看通过并发、并行和异步加速 Python 文章

Scenario 设想

With that, let’s take a look at how to speed up the following tasks: 接下来,让我们看看如何加快以下任务的速度:

# tasks.py

import os
from multiprocessing import current_process
from threading import current_thread

import requests

def make_request(num):
    # io-bound

    pid = os.getpid()
    thread_name = current_thread().name
    process_name = current_process().name
    print(f"{pid} - {process_name} - {thread_name}")

    requests.get("https://httpbin.org/ip")

async def make_request_async(num, client):
    # io-bound

    pid = os.getpid()
    thread_name = current_thread().name
    process_name = current_process().name
    print(f"{pid} - {process_name} - {thread_name}")

    await client.get("https://httpbin.org/ip")

def get_prime_numbers(num):
    # cpu-bound

    pid = os.getpid()
    thread_name = current_thread().name
    process_name = current_process().name
    print(f"{pid} - {process_name} - {thread_name}")

    numbers = []

    prime = [True for i in range(num + 1)]
    p = 2

    while p * p <= num:
        if prime[p]:
            for i in range(p * 2, num + 1, p):
                prime[i] = False
        p += 1

    prime[0] = False
    prime[1] = False

    for p in range(num + 1):
        if prime[p]:
            numbers.append(p)

    return numbers

All of the code examples in this tutorial can be found in the parallel-concurrent-examples-python repo.z 本教程中的所有代码示例都可以在parallel-concurrent-examples-python 存储库中找到。

Notes: 笔记(上面代码中的函数说明):

  • make_request makes an HTTP request to httpbin.org/ip X number of times.
  • make_requesthttpbin.org/ip 发出 HTTP 请求 X 次。
  • make_request_async makes the same HTTP request asynchronously with HTTPX.
  • make_request_async 使用 HTTPX 异步(asynchronously )发出相同的 HTTP 请求。
  • get_prime_numbers calculates the prime numbers, via the Sieve of Eratosthenes method, from two to the provided limit.
  • get_prime_numbers 通过埃拉托斯特尼筛法计算从 2 到 指定数值 间的素数。

We’ll be using the following libraries from the standard library to speed up the above tasks: 我们将使用标准库中的以下库来加速上述任务:

  • threading for running tasks concurrently

  • threading线程用于并发运行任务的

  • multiprocessing for running tasks in parallel

  • multiprocessing 多进程并行运行任务的

  • concurrent.futures for running tasks concurrently and in parallel from a single interface

  • concurrent.futures 异步并发 用于并发运行任务,以及从单个接口并行

    (注:concurrent.futures是重要的异步编程库。内部实现机制非常复杂,简单来说就是开辟一个固定大小为n的进程池/线程池。进程池中最多执行n个进程/线程,当任务完成后,从任务队列中取新任务。若池满,则排队等待。)

  • asyncio for running tasks concurrency with coroutines managed by the Python interpreter

  • asyncio 异步IO用于使用 Python 解释器管理的协程 coroutines 并发运行任务

Library 图书馆Class/Method 类/方法Processing Type 加工类型
threading 线程Thread 线程concurrent 并发
concurrent.futures 异步并发ThreadPoolExecutor 线程池执行器concurrent 并发
asyncio 异步IOgather 收集concurrent (via coroutines) 并发(通过协程)
multiprocessing 多进程Pool 池parallel 并行
concurrent.futures 异步并发ProcessPoolExecutor 进程池执行器parallel 并行

IO-bound Operation IO 绑定操作

Again, IO-bound tasks spend more time on IO than on the CPU. 同样,IO 密集型任务在 IO 上花费的时间比在 CPU 上花费的时间更多。

Since web scraping is IO bound, we should use threading to speed up the processing as the retrieving of the HTML (IO) is slower than parsing it (CPU). 由于网页抓取受 IO 限制,因此我们应该使用线程(threading )来加快处理速度,因为 HTML 的检索 (IO) 比解析它 (CPU) 慢。

Scenario: How to speed up a Python-based web scraping and crawling script? 场景:如何加速基于Python的网页抓取和爬行脚本?

Sync Example 同步示例

Let’s start with a benchmark. 让我们从一个基准测试程序(benchmark)开始。

# io-bound_sync.py

import time

from tasks import make_request

def main():
    for num in range(1, 101):
        make_request(num)

if __name__ == "__main__":
    start_time = time.perf_counter()

    main()

    end_time = time.perf_counter()
    print(f"Elapsed run time: {end_time - start_time} seconds.")

Here, we made 100 HTTP requests using the make_request function. Since requests happen synchronously, each task is executed sequentially. 在这里,我们使用 make_request 函数发出 100 个 HTTP 请求。由于请求是同步(synchronously)发生的,因此每个任务都是按顺序执行的。

Elapsed run time: 15.710984757 seconds.

So, that’s roughly 0.16 seconds per request. 因此,每个请求大约需要 0.16 秒。

Threading Example 线程示例

# io-bound_concurrent_1.py

import threading
import time

from tasks import make_request

def main():
    tasks = []

    for num in range(1, 101):
        tasks.append(threading.Thread(target=make_request, args=(num,)))
        tasks[-1].start()

    for task in tasks:
        task.join()

if __name__ == "__main__":
    start_time = time.perf_counter()

    main()

    end_time = time.perf_counter()
    print(f"Elapsed run time: {end_time - start_time} seconds.")

Here, the same make_request function is called 100 times. This time the threading library is used to create a thread for each request. 此处,相同的 make_request 函数被调用 100 次。这次使用 threading 库为每个请求创建一个线程。

Elapsed run time: 1.020112515 seconds.

The total time decreases from ~16s to ~1s. 总时间从约 16 秒减少到约 1 秒。

Since we’re using separate threads for each request, you might be wondering why the whole thing didn’t take ~0.16s to finish. This extra time is the overhead for managing threads. The Global Interpreter Lock (GIL) in Python makes sure that only one thread uses the Python bytecode at a time. 由于我们对每个请求使用单独的线程,因此您可能想知道为什么整个事情不是花费大约 0.16 秒(16 秒/100个线程=0.16 秒)完成。这个额外的耗费时间就是管理线程的开销。 Python 中的全局解释器锁 (GIL) 确保一次只有一个线程使用 Python 字节码。

concurrent.futures Example concurrent.futures示例

# io-bound_concurrent_2.py

import time
from concurrent.futures import ThreadPoolExecutor, wait

from tasks import make_request

def main():
    futures = []

    with ThreadPoolExecutor() as executor:
        for num in range(1, 101):
            futures.append(executor.submit(make_request, num))

    wait(futures)

if __name__ == "__main__":
    start_time = time.perf_counter()

    main()

    end_time = time.perf_counter()
    print(f"Elapsed run time: {end_time - start_time} seconds.")

Here we used concurrent.futures.ThreadPoolExecutor to achieve multithreading. After all the futures/promises are created, we used wait to wait for all of them to complete. 这里我们使用 concurrent.futures.ThreadPoolExecutor 来实现多线程。创建所有 futures/promise 后,我们使用 wait 等待它们全部完成。

Elapsed run time: 1.340592231 seconds

concurrent.futures.ThreadPoolExecutor is actually an abstraction around the multithreading library, which makes it easier to use. In the previous example, we assigned each request to a thread and in total 100 threads were used. But ThreadPoolExecutor defaults the number of worker threads to min(32, os.cpu_count() + 4). ThreadPoolExecutor exists to ease the process of achieving multithreading. If you want more control over multithreading, use the multithreading library instead. concurrent.futures.ThreadPoolExecutor 实际上是对 multithreading 库的抽象,这使得它更易于使用。在前面的示例中,我们将每个请求分配给一个线程,总共使用了 100 个线程。但 ThreadPoolExecutor 默认工作线程数为 min(32, os.cpu_count() + 4) 。 ThreadPoolExecutor 的存在是为了简化实现多线程的过程。如果您想要对多线程进行更多控制,请改用 multithreading 库。

AsyncIO Example 异步IO示例

# io-bound_concurrent_3.py

import asyncio
import time

import httpx

from tasks import make_request_async

async def main():
    async with httpx.AsyncClient() as client:
        return await asyncio.gather(
            *[make_request_async(num, client) for num in range(1, 101)]
        )

if __name__ == "__main__":
    start_time = time.perf_counter()

    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

    end_time = time.perf_counter()
    elapsed_time = end_time - start_time
    print(f"Elapsed run time: 0.0583 seconds")

httpx is used here since requests does not support async operations. 此处使用 httpx ,因为 requests 不支持异步操作。

Here, we used asyncio to achieve concurrency. 这里,我们使用 asyncio 来实现并发。

Elapsed run time: 0.553961068 seconds

asyncio is faster than the other methods, because threading makes use of OS (Operating System) threads. So the threads are managed by the OS, where thread switching is preempted by the OS. asyncio uses coroutines, which are defined by the Python interpreter. With coroutines, the program decides when to switch tasks in an optimal way. This is handled by the even_loop in asyncio. asyncio 比其他方法更快,因为 threading 使用 OS(操作系统)线程。因此线程由操作系统管理,其中线程切换由操作系统抢占。 asyncio 使用由 Python 解释器定义的协程。通过协程,程序可以决定何时以最佳方式切换任务。这是由 asyncio 中的 even_loop 处理的。

CPU-bound Operation CPU 密集型操作

Scenario: How to speed up a simple data processing script? 场景:如何加速一个简单的数据处理脚本?

Sync Example 同步示例

Again, let’s start with a benchmark. 再次,让我们从基准测试程序开始。

# cpu-bound_sync.py

import time

from tasks import get_prime_numbers

def main():
    for num in range(1000, 16000):
        get_prime_numbers(num)

if __name__ == "__main__":
    start_time = time.perf_counter()

    main()

    end_time = time.perf_counter()
    print(f"Elapsed run time: {end_time - start_time} seconds.")

Here, we executed the get_prime_numbers function for numbers from 1000 to 16000. 在这里,我们对 1000 到 16000 之间的数字执行 get_prime_numbers 函数。

Elapsed run time: 17.863046316 seconds.

Multiprocessing Example 多进程示例

# cpu-bound_parallel_1.py

import time
from multiprocessing import Pool, cpu_count

from tasks import get_prime_numbers

def main():
    with Pool(cpu_count() - 1) as p:
        p.starmap(get_prime_numbers, zip(range(1000, 16000)))
        p.close()
        p.join()

if __name__ == "__main__":
    start_time = time.perf_counter()

    main()

    end_time = time.perf_counter()
    print(f"Elapsed run time: {end_time - start_time} seconds.")

Here, we used multiprocessing to calculate the prime numbers. 在这里,我们使用 multiprocessing 来计算素数。

Elapsed run time: 2.9848740599999997 seconds.

concurrent.futures Example concurrent.futures示例

# cpu-bound_parallel_2.py

import time
from concurrent.futures import ProcessPoolExecutor, wait
from multiprocessing import cpu_count

from tasks import get_prime_numbers

def main():
    futures = []

    with ProcessPoolExecutor(cpu_count() - 1) as executor:
        for num in range(1000, 16000):
            futures.append(executor.submit(get_prime_numbers, num))

    wait(futures)

if __name__ == "__main__":
    start_time = time.perf_counter()

    main()

    end_time = time.perf_counter()
    print(f"Elapsed run time: {end_time - start_time} seconds.")

Here, we achieved multiprocessing using concurrent.futures.ProcessPoolExecutor. Once the jobs are added to futures, wait(futures) waits for them to finish. 在这里,我们使用 concurrent.futures.ProcessPoolExecutor 实现了多处理。将作业添加到 future 后, wait(futures) 就会等待它们完成。

Elapsed run time: 4.452427557 seconds.

concurrent.futures.ProcessPoolExecutor is a wrapper around multiprocessing.Pool. It has the same limitations as the ThreadPoolExecutor. If you want more control over multiprocessing, use multiprocessing.Pool. concurrent.futures provides an abstraction over both multiprocessing and threading, making it easy to switch between the two. concurrent.futures.ProcessPoolExecutormultiprocessing.Pool 的包装器。它具有与 ThreadPoolExecutor 相同的限制。如果您想更好地控制多处理,请使用 multiprocessing.Poolconcurrent.futures 提供了对多处理和线程的抽象,使得在两者之间切换变得容易。

Conclusion 结论

It’s worth noting that using multiprocessing to execute the make_request function will be much slower than the threading flavor since the processes will be need to wait for the IO. The multiprocessing approach will be faster then the sync approach, though. 值得注意的是,使用多进程来执行 make_request 函数将比线程的方式慢得多,因为进程需要等待 IO。不过,多进程方法还是会比同步方式更快。

Similarly, using concurrency for CPU-bound tasks is not worth the effort when compared to parallelism. 同样,与并行相比,对 CPU 密集型任务使用并发并不值得。

That being said, using concurrency or parallelism to execute your scripts adds complexity. Your code will generally be harder to read, test, and debug, so only use them when absolutely necessary for long-running scripts. 话虽这么说,使用并发或并行来执行脚本会增加复杂性。您的代码通常更难阅读、测试和调试,因此仅在长时间运行的脚本绝对必要时才使用它们。

concurrent.futures is where I generally start since- concurrent.futures 是我通常开始的地方,因为-

  1. It’s easy to switch back and forth between concurrency and parallelism
  2. 可以轻松地在并发和并行之间来回切换
  3. The dependent libraries don’t need to support asyncio (requests vs httpx)
  4. 依赖库不需要支持 asyncio ( requestshttpx
  5. It’s cleaner and easier to read over the other approaches
  6. 与其他方法相比,它更干净、更容易阅读

Grab the code from the parallel-concurrent-examples-python repo on GitHub. 从 GitHub 上的 parallel-concurrent-examples-python 存储库获取代码。

---------------------------END---------------------------

题外话

感谢你能看到最后,给大家准备了一些福利!

感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。


👉优快云大礼包🎁:全网最全《Python学习资料》免费赠送🆓!(安全链接,放心点击)

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

img

二、Python兼职渠道推荐*

学的同时助你创收,每天花1-2小时兼职,轻松稿定生活费.
在这里插入图片描述

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

img

四、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

img

👉 优快云大礼包:gift::[全网最全《Python学习资料》免费赠送:free:!](https://blog.youkuaiyun.com/weixin_68789096/article/details/132275547?spm=1001.2014.3001.5502) (安全链接,放心点击)

若有侵权,请联系删除

<think>我们正在讨论异步通信同步通信的区别及示例。根据引用资料,我们可以总结如下: 1. **同步通信**: - 发送方发送数据后,必须等待接收方的响应才能继续下一步操作。 - 采用阻塞调用方式,即发送方在等待响应时会被阻塞,不能执行其他任务。 - 实时较高,适合数据量小且要求即时响应的场景。 2. **异步通信**: - 发送方发送数据后,不需要立即等待响应,可以继续处理其他任务。 - 当响应到达时,系统会通过回调、事件或消息等方式通知发送方。 - 适合数据量大或处理时间较长的场景,能提高系统的吞吐量响应。 接下来,我们将通过示例来进一步说明这两种通信方式。 ### 示例1:同步通信示例(HTTP请求) 在同步通信中,客户端发送请求后会一直等待服务器响应。以下是一个使用Python的`requests`库进行同步HTTP请求的示例: ```python import requests # 同步HTTP GET请求 response = requests.get('https://api.example.com/data') # 发送请求并等待响应 print(response.json()) # 处理响应数据 ``` 在这个例子中,程序在`requests.get()`调用处阻塞,直到收到服务器的响应才会继续执行后面的`print`语句。 ### 示例2:异步通信示例(使用asyncioaiohttp) 在异步通信中,客户端发送请求后不会阻塞,而是继续执行其他任务。当响应到达时,再处理响应。以下是一个使用Python的`asyncio``aiohttp`库的异步HTTP请求示例: ```python import aiohttp import asyncio async def fetch_data(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.json() async def main(): url = 'https://api.example.com/data' task = asyncio.create_task(fetch_data(url)) # 创建异步任务 print("继续执行其他操作...") # 在等待响应期间可以执行其他任务 data = await task # 等待任务完成(非阻塞点,可切换任务) print(data) asyncio.run(main()) ``` 在这个例子中,当`fetch_data`函数发送请求后,程序不会阻塞在`session.get()`处,而是可以继续执行`main`函数中的`print("继续执行其他操作...")`。当响应到达时,`fetch_data`会处理响应,然后通过`await task`获取结果。 ### 区别总结 | **特** | **同步通信** | **异步通信** | |------------------|----------------------------------|----------------------------------| | **等待响应** | 发送后必须等待响应 | 发送后无需等待,可继续其他任务 | | **阻塞** | 阻塞调用 | 非阻塞调用 | | **实时** | 较高,适合即时交互 | 较低,适合后台任务 | | **资源利用率** | 较低(线程/进程可能被阻塞) | 较高(可并发处理多个任务) | | **复杂度** | 简单,易于理解 | 较复杂,需要回调或异步机制 | | **适用场景** | 小数据量、即时响应(如银行交易) | 大数据量、耗时操作(如文件上传) | ### 实际应用场景 - **同步通信**:适用于需要即时确认的场景,例如: - 银行转账(必须立即知道成功与否) - 用户登录验证(需要立即反馈结果)[^2][^4] - **异步通信**:适用于耗时操作或不需要即时响应的场景,例如: - 消息队列(如RabbitMQ、Kafka) - 后台任务处理(如发送邮件、生成报告)[^2][^4] 在串口通信(如UART/USART)中,异步模式(如UART)不需要时钟线,而同步模式(如USART的同步模式)需要时钟线来同步数据传输。异步模式适用于简单、低速的设备通信(如传感器数据读取),而同步模式适用于高速、可靠的数据传输(如SPI、I2C)[^3]。 通过以上对比示例,我们可以根据实际需求选择合适的通信方式。同步通信简单可靠,但会阻塞进程;异步通信高效灵活,但编程模型复杂。 §§相关问题§§ 1. 异步通信中如何处理回调地狱问题? 2. 同步通信在分布式系统中的局限是什么? 3. 串口通信中异步模式同步模式的硬件接线有何不同? 4. 如何选择消息队列实现异步通信? 5. 异步编程模型有哪些常见的设计模式?</think>### 异步通信与同步通信的区别及示例 #### 核心区别对比 | **特** | **同步通信** | **异步通信** | |------------------|----------------------------------|----------------------------------| | **时序要求** | 严格时钟同步 | 无时钟同步 | | **数据传输方式** | 连续数据块传输 | 按字符/帧独立传输 | | **响应机制** | 发送后必须等待响应 | 发送后无需等待响应 | | **阻塞** | 阻塞式(发送方等待) | 非阻塞式(发送方继续工作) | | **硬件要求** | 需要时钟线 | 无需时钟线 | | **实时** | 高实时 | 较低实时 | | **适用场景** | 高速大数据量传输 | 低速设备/简单连接 | --- #### 1. **同步通信详解** **工作原理**: 发送方接收方共享同一时钟信号,数据以连续块的形式传输。发送方发送数据后会阻塞等待接收方的确认响应,才能继续后续操作。 **核心特点**: - 必须有时钟同步线(如SPI的SCK,I2C的SCL) - 数据以固定长度的帧连续传输 - 高带宽利用率,适合大数据量传输 - 实时强,延迟可预测 **示例场景**: **SPI通信(微控制器与存储器)** ```c // STM32 SPI同步传输示例 HAL_SPI_Transmit(&hspi1, tx_data, sizeof(tx_data), HAL_MAX_DELAY); // 阻塞等待传输完成 HAL_SPI_Receive(&hspi1, rx_data, sizeof(rx_data), HAL_MAX_DELAY); // 阻塞等待接收完成 ``` 硬件连接: ``` 主设备 从设备 MOSI ───► MOSI MISO ◄─── MISO SCK ───► SCK // 同步时钟线 CS ───► CS // 片选信号 ``` 此模式下主设备控制时钟,从设备必须在时钟边沿响应[^3][^4]。 --- #### 2. **异步通信详解** **工作原理**: 每个数据单元(字符/帧)独立传输,包含起始位停止位标识边界。发送方发送数据后立即释放资源,响应通过回调或事件机制处理。 **核心特点**: - 无共享时钟线(如UART仅需TX/RX) - 每个字符自带同步位(起始位+停止位) - 发送后不阻塞,可并行处理其他任务 - 抗干扰能力强,适合长距离传输 **示例场景**: **UART串口通信(单片机与PC)** ```python # Python 异步串口读取示例 import serial import threading def callback(data): print("Received:", data.decode()) # 收到数据后处理 ser = serial.Serial('COM3', 9600, timeout=0) # 异步读取线程 def read_thread(): while True: if ser.in_waiting: data = ser.read(ser.in_waiting) callback(data) # 非阻塞回调 threading.Thread(target=read_thread, daemon=True).start() ser.write(b'Hello PC!') # 发送后立即继续执行 ``` 硬件连接: ``` 单片机 PC TX ───► RX // 无需时钟线 RX ◄─── TX GND ──── GND ``` 发送方写入数据后无需等待,数据到达时通过中断触发接收处理[^1][^3]。 --- #### 3. **选择建议** | **场景** | **推荐方式** | **理由** | |------------------------------|--------------|-----------------------------------| | 高速传感器数据采集(如摄像头)| 同步通信 | 大数据量需高吞吐率[^2] | | 设备状态监控(如温度传感器) | 异步通信 | 低频数据+简单布线[^3] | | 金融交易系统 | 同步通信 | 需要即时确认[^4] | | 消息队列系统 | 异步通信 | 解耦生产者消费者[^2] | | 实时控制系统(如机器人) | 同步通信 | 严格时序要求[^1] | | IoT设备通信 | 异步通信 | 低功耗+不稳定网络环境[^3] | > **关键结论**:同步通信像"电话会议"(实时互动),异步通信像"发邮件"(发送后继续工作)[^4]。在STM32等MCU开发中,同步模式用于高速外设(SPI/I2C),异步模式用于串口调试传感器连接[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值