【python学习】多线程编程的背景、定义、特点、优缺点、使用场景和示例以及和单线程的区别

引言

随着计算机技术的发展,多核处理器已经成为了主流,为了充分利用多核处理器带来的并行计算能力,提高程序的执行效率和响应速度,多线程编程变得尤为重要
Python作为一种高级编程语言,提供了多线程编程的支持,允许开发者创建能够在后台执行任务的线程,从而实现程序的并发执行

一、定义

Python多线程编程是指在Python程序中创建并管理多个线程的过程
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位
Python中,通常使用threading模块来创建和管理线程。

二、特点

2.1 并发性

线程可以在同一程序内并发执行,使得程序能够同时处理多个任务

2.2 资源共享

线程共享进程的内存和资源,因此它们之间通信和数据共享相对容易

2.3 轻量级

线程比进程更加轻量级,创建和销毁线程的开销小于进程

三、优点

3.1 提高程序响应性

在执行I/O密集型任务时,多线程可以让程序在等待I/O操作完成的同时继续执行其他任务。

3.2 改善资源利用率

由于线程共享内存和资源,可以减少资源消耗。

3.3 简化程序结构

通过将复杂的任务分解为多个线程,可以使程序结构更加清晰。

四、缺点

4.1 全局解释器锁(GIL)

Python中,由于GIL的存在,即使在多核处理器上,同一时间也只能有一个线程执行Python字节码,限制了线程的并行执行能力

4.2 线程安全问题

线程共享内存可能导致数据竞争和同步问题,需要额外的机制来保证线程安全

4.3 调试困难

线程间的交互可能导致难以追踪和重现的问题

五、使用场景

5.1 I/O密集型任务

如网络请求、文件读写等,线程在等待I/O操作时可以释放GIL,让其他线程运行

5.2 图形用户界面(GUI)应用

在GUI应用中,主线程通常用于处理用户界面事件,而多线程可以用于后台任务,以保持界面的响应性

5.3 并行任务分解

可以将一个大任务分解为多个小任务,由不同的线程并行执行

六、示例

以下是一个简单的Python多线程编程示例,演示了如何在程序中创建两个线程,分别执行不同的任务

import threading
import time
# 定义一个函数供线程执行
def print_numbers():
    for i in range(1, 10):
        print(i)
        time.sleep(0.5)  # 模拟I/O操作
# 定义另一个函数供线程执行
def print_letters():
    for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
        print(letter)
        time.sleep(0.5)  # 模拟I/O操作
# 创建两个线程
thread1 = threading.Thread(target=print_numbers)
thread2 = threading.Thread(target=print_letters)
# 启动线程
thread1.start()
thread2.start()
# 等待线程结束
thread1.join()
thread2.join()
print("线程执行完毕。")

在这个示例中,我们定义了两个函数print_numbersprint_letters,分别用于打印数字和字母。每个函数都包含一个time.sleep调用,模拟I/O操作。我们创建了两个线程thread1thread2,分别执行这两个函数。通过调用start()方法启动线程,并通过join()方法等待线程执行结束。这个例子展示了如何在Python中使用多线程来并发执行任务。

七、多线程编程与单线程的区别

7.1 执行模型

7.1.1 单线程编程

在单线程编程中,程序按照代码的顺序依次执行,一次只能执行一个任务。如果当前任务需要等待(如I/O操作),整个程序都会等待,直到该任务完成。

7.1.2 多线程编程

多线程编程允许程序同时执行多个线程,每个线程可以看作是一个独立的执行流。这使得程序能够在等待一个线程执行I/O操作时,在另一个线程中继续执行其他任务。

7.2并行与并发

7.2.1 单线程编程

单线程程序通常是顺序执行的,即使在多核处理器上,也不能真正实现并行计算。

7.2.2 多线程编程

多线程程序可以在多核处理器上实现真正的并行计算,尽管在CPython中由于全局解释器锁(GIL)的存在,纯Python代码的并行执行受到限制。

7.3 性能

7.3.1 单线程编程

对于CPU密集型任务,单线程程序通常能够充分利用单个CPU核心的性能。对于I/O密集型任务,单线程程序可能会因为等待I/O操作而造成性能瓶颈。

7.3.2 多线程编程

对于I/O密集型任务,多线程可以提高程序的响应性和性能,因为它可以在一个线程等待I/O操作时,让其他线程继续执行。然而,对于CPU密集型任务,多线程在CPython中可能不会带来性能上的提升,甚至可能因为线程切换和GIL的存在而导致性能下降。

7.4 复杂性

7.4.1 单线程编程

单线程程序的逻辑通常更简单,因为它们顺序执行,没有线程之间的交互和同步问题。

7.4.2 多线程编程

多线程程序更加复杂,因为需要处理线程之间的资源共享、同步(如使用锁、信号量等)以及可能的死锁和竞态条件。

7.5 同步和线程安全

7.5.1 单线程编程

在单线程环境中,不需要担心线程安全问题,因为只有一个线程在操作数据。

7.5.2 多线程编程

在多线程环境中,必须确保线程安全,防止多个线程同时修改同一数据,导致数据不一致或程序崩溃。

7.6 示例对比

以下是一个单线程与多线程的简单对比示例:

7.6.1 单线程示例

import time
def task(name, delay):
    for i in range(5):
        print(f"Task {name}: Iteration {i}")
        time.sleep(delay)
start_time = time.time()
task("A", 1)
task("B", 2)
end_time = time.time()
print(f"Total time: {end_time - start_time} seconds")

在这个单线程示例中,任务A和任务B将依次执行,总执行时间将是两个任务执行时间的总和。

7.6.2 多线程示例

import threading
import time
def task(name, delay):
    for i in range(5):
        print(f"Task {name}: Iteration {i}")
        time.sleep(delay)
start_time = time.time()
thread1 = threading.Thread(target=task, args=("A", 1))
thread2 = threading.Thread(target=task, args=("B", 2))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
end_time = time.time()
print(f"Total time: {end_time - start_time} seconds")

在这个多线程示例中,任务A和任务B将并发执行,总执行时间将接近于执行时间较长的那个任务,因为它们是并行执行的。

总结来说,多线程编程可以提升程序的并发性和响应性,但也带来了额外的复杂性和线程安全问题
单线程编程则更加简单,但无法充分利用多核处理器的能力。选择多线程还是单线程编程取决于具体的应用场景和需求。

八、总结(思维导图)

在这里插入图片描述

PS:知识内容部分取自:https://www.runoob.com/python/python-multithreading.html

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python是一种灵活的编程语言,其提供了多种多样的库和框架,以方便用户处理数据和进行网络爬取。在网络爬取方面,Python具有优秀的单线程多线程爬取能力。 Python单线程爬取实例: 当我们需要爬取一个简单的网站时,单线程爬取可能是最简单和最有效的方法。例如,我们可以编写一个程序来爬取一个网站的所有页面,并将它们保存到本地文件夹中。这个程序可能像这样: ```Python import requests from bs4 import BeautifulSoup def getUrls(url): response = requests.get(url) soup = BeautifulSoup(response.text, 'html.parser') urls = [] for link in soup.find_all('a'): urls.append(link.get('href')) return urls def download(urls): for url in urls: response = requests.get(url) filename = url.split('/')[-1] with open(filename, 'wb') as f: f.write(response.content) if __name__ == '__main__': urls = getUrls('http://example.com') download(urls) ``` 在这个例子中,我们使用requests和BeautifulSoup库来获取和解析HTML页面,然后使用循环和文件I/O来保存页面内容。 Python多线程爬取实例: 当我们需要爬取大量页面时,单线程爬取可能会非常缓慢,因此我们可以使用多线程爬取来提高效率。例如,我们可以使用Python多线程库threading来实现多线程爬取。下面是代码示例: ```Python import requests from bs4 import BeautifulSoup import threading def getUrls(url): response = requests.get(url) soup = BeautifulSoup(response.text, 'html.parser') urls = [] for link in soup.find_all('a'): urls.append(link.get('href')) return urls def download(url): response = requests.get(url) filename = url.split('/')[-1] with open(filename, 'wb') as f: f.write(response.content) class CrawlerThread(threading.Thread): def __init__(self, url): threading.Thread.__init__(self) self.url = url def run(self): download(self.url) if __name__ == '__main__': urls = getUrls('http://example.com') threads = [] for url in urls: t = CrawlerThread(url) threads.append(t) t.start() for t in threads: t.join() ``` 在这个例子中,我们使用多线程CrawlerThread类来下载每个页面。我们创建一个CrawlerThread列表,然后将列表中的每个元素作为参数传递给download函数,以便每个线程都可以执行下载任务。最后,我们使用join方法等待所有线程完成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值