Python基础教程之多线程编程详解

 更新时间:2023年06月15日 09:20:04   作者:陆理手记  
Python支持多线程编程和多进程编程,在本教程中,我们将学习有关Python多线程编程的基础知识、线程同步、线程池以及如何使用多进程,需要的朋友可以参考下

进程(process)指的是正在运行的程序的实例,当我们执行某个程序时,进程就被操作系统创建了。而线程(thread)则包含于进程之中,是操作系统能够进行运算调度的最小单元,多个线程可以同处一个进程中,且同时处理不同的任务。一条进程中可以并发多个线程,而同一条线程将共享该进程中的全部系统资源。

每个进程都有自己独立的地址空间、内存和数据栈,因此进程之间通讯不方便,所以需使用用进程间通讯(InterProcess Communication, IPC)。而同一个进程中的线程共享资源,因此线程间通讯非常方便,只需注意数据同步与互斥的问题。

1. 多线程基础

线程是进程内的执行单元,每个进程都至少有一个线程。Python标准库提供了thread模块和threading模块来支持线程编程。在Python3中,thread模块已经改名为_thread,并在_thread基础上开发了更为强大的threading模块,因此我们可以使用threading.Thread类创建线程对象,并通过start()方法启动线程。

import threading
def worker():
    print("I am a thread.")
t = threading.Thread(target=worker)
t.start()

在上面的代码中,我们创建了一个名为worker的函数,它是线程的工作内容。我们使用threading.Thread类创建了一个线程对象t,并将worker作为参数传递给了Thread构造函数。然后,我们调用t.start()方法来启动线程。当我们运行这段代码时,会看到以下输出:

I am a thread.

另外,我们也可以通过继承Thread类并重写run方法来实现线程的创建.

import threading
class MyThread(threading.Thread):
	def __init__(self, n):
		self.n = n
		super().__init__() # 调用父类函数初始化线程
	def run(self):
		print('线程:', self.n)
for i in range(1,3):
	t = MyThread(i)
	t.start()

果应该输出打印出:

线程1
线程2

2. 多线程同步

当多个线程同时访问共享资源时,可能会发生竞态条件。为避免这种情况,我们需要使用线程同步机制。Python提供了锁(Lock)、信号量(Semaphore)、事件(Event)等机制来实现线程同步。

下面是一个使用锁来确保线程同步的示例:

import threading
lock = threading.Lock()
count = 0
def worker():
    global count
    lock.acquire()
    try:
        count += 1
    finally:
        lock.release()
threads = []
for i in range(10):
    t = threading.Thread(target=worker)
    threads.append(t)
for t in threads:
    t.start()
for t in threads:
    t.join()
print(count)

在上面的代码中,我们定义了一个全局变量count和一个锁对象lockworker函数是每个线程的工作内容,它通过lock来确保count的修改是原子性的。我们创建了10个线程并启动它们,等待所有线程执行完毕后输出count的值。当我们运行这段代码时,会看到以下输出:

10

如果把锁去掉后,count的值会是几呢?动手试试吧!会有意向不到的结果哦~

3. 线程池

线程池可以用来提高代码的性能,在需要大量线程处理任务时,可以使用线程池来减少线程的创建和销毁次数,提高线程的复用率。

Python标准库提供了concurrent.futures模块来支持线程池编程。我们可以使用ThreadPoolExecutor类来创建和管理线程池。

下面是一个使用线程池来处理任务的示例:

from concurrent.futures import ThreadPoolExecutor
def worker(num):
    return num * num
executor = ThreadPoolExecutor(max_workers=4)
results = []
for i in range(10):
    result = executor.submit(worker, i)
    results.append(result)
for result in results:
    print(result.result())

在上面的代码中,我们定义了一个worker函数用来处理任务。我们创建了一个ThreadPoolExecutor对象executor,并将最大工作线程数设置为4。然后,我们使用submit方法提交10个任务给executor,并将结果保存在results列表中。最后,我们遍历results列表并输出每个任务的结果。

看到这些个熟悉的单词...恍惚间有种写java代码的感觉。

4. 多进程编程

进程是计算机中程序执行的基本单位,而线程则是进程中执行代码的单位。多进程编程就是利用多个独立运行的进程来完成任务。相比单进程,多进程有以下优点:

  • 提高了程序的并发性能。
  • 可以更好地利用多核CPU。
  • 能够处理更大的数据集。

在进行多进程编程时,需要注意进程之间的通信和同步问题。

5. Python实现多进程编程

Python标准库中的multiprocessing模块提供了实现多进程编程的工具。其中常用的方法包括:

  • Process:创建一个新进程。
  • Pool:创建一组进程池。
  • Queue:进程之间的消息队列。

下面是一个简单的示例,展示如何使用multiprocessing模块创建子进程:

import multiprocessing
def worker():
    """子进程要执行的任务"""
    print('子进程正在运行')
if __name__ == '__main__':
    p = multiprocessing.Process(target=worker)
    p.start()
    print('主进程已经结束')

在这个例子中,我们创建一个新的进程,并将其target属性指向worker函数。然后使用start方法启动该进程。注意到在Windows系统中,需要将启动进程的代码放在if __name__ == '__main__':语句内,以避免出现一些问题。

5.1 Python进程池

import multiprocessing
def worker(num):
    """子进程要执行的任务"""
    print(f'子进程{num}正在运行')
if __name__ == '__main__':
    with multiprocessing.Pool(processes=4) as pool:
        results = pool.map(worker, range(4))
        print(results)

在这个例子中,我们使用了进程池来管理多个子进程。首先创建一个Pool对象,其中processes参数指定了进程池的大小。然后使用map方法提交任务,该方法会自动分配任务给空闲的子进程并返回结果。最后打印输出结果。

5.2 在进程之间进行通信

import multiprocessing
def writer(q):
    """写入数据"""
    for i in range(10):
        q.put(i)
def reader(q):
    """读取数据"""
    while True:
        item = q.get()
        if item is None:
            break
        print(item)
if __name__ == '__main__':
    q = multiprocessing.Queue()
    # 启动子进程
    p1 = multiprocessing.Process(target=writer, args=(q,))
    p2 = multiprocessing.Process(target=reader, args=(q,))
    p1.start()
    p2.start()
    # 等待子进程结束
    p1.join()
    q.put(None)
    p2.join()

在这个例子中,我们创建了一个消息队列,并通过Queue对象将其传递给两个子进程。一个子进程负责将数据写入队列中,而另一个子进程则从队列中读取数据并输出到屏幕上。注意到在程序末尾我们向队列中添加了None元素,以便通知读取数据的子进程结束循环。

6.多进程计算圆周率

下面是使用Python多进程和蒙特卡罗(Monte Carlo)方法估计圆周率的示例代码:

import random
from multiprocessing import Pool
def estimate_pi(n):
    num_points_inside_circle = 0
    for _ in range(n):
        x = random.uniform(-1, 1)
        y = random.uniform(-1, 1)
        if x**2 + y**2 <= 1:
            num_points_inside_circle += 1
    return 4 * num_points_inside_circle / n
if __name__ == '__main__':
    num_processes = 4
    num_samples = int(1e8)
    with Pool(num_processes) as p:
        results = p.map(estimate_pi, [int(num_samples/num_processes)] * num_processes)
    print(sum(results)/num_processes)

该代码使用random模块中的uniform函数生成在[−1,1][-1, 1][−1,1]范围内均匀分布的随机数,并统计落入以原点为圆心,半径为1的圆中的点数。最后,将所有子进程的结果相加并除以进程数以得到圆周率的估计值。我算出的结果是3.1416664800000005, 你们呢?

其中,Pool(num_processes) 创建一个具有num_processes个进程的进程池,p.map(estimate_pi, [int(num_samples/num_processes)] * num_processes) 在每个进程上调用estimate_pi函数,并将输入参数设为所需的样本数量的四分之一,然后返回其结果。

7.总结

在Python多线程与进程编程中,我们不仅要理解和掌握的关于threading 模块和multiprocessing模块如何使用,更要掌握的是线程和进程之间的区别和联系以及线程通信、进程通信的方式。

Python提供了丰富的多线程和多进程编程工具,使我们能够更有效地利用计算机的多核心处理能力。线程同步和线程池等同步原语可以帮助我们避免竞争条件,并提高程序性能。使用好多线程和多进程,可以使Python编程变得更加高效和灵活。Python爬虫应用中,经常会大量使用线程和进程来提升抓取效率哦

以上就是Python基础教程之多线程编程详解的详细内容,更多关于Python多线程编程的资料请关注脚本之家其它相关文章!

相关文章

  • Python实现多维数据分析的示例详解

    Python实现多维数据分析的示例详解

    多维数据分析是对数据的信息分析,它考虑了许多关系,这篇文章主要为大家详细介绍了一些使用Python分析多维/多变量数据的基本技术,希望对大家有所帮助
    2023-11-11
  • pytorch教程之网络的构建流程笔记

    pytorch教程之网络的构建流程笔记

    这篇文章主要介绍了pytorch教程中网络的构建流程,文中附含了详细的示例代码流程,有需要的朋友可以借鉴参考下,希望可以有所帮助
    2021-09-09
  • Pycharm及python安装详细教程(图解)

    Pycharm及python安装详细教程(图解)

    这篇文章主要介绍了Pycharm及python安装详细教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2020-07-07
  • Django的ALLOWED_HOSTS配置方法

    Django的ALLOWED_HOSTS配置方法

    allowed_hosts 参数是用于设置 Django 的域名白名单的,本文主要介绍了Django的ALLOWED_HOSTS配置方法,具有一定的参考价值,感兴趣的可以了解一下
    2024-06-06
  • python全栈知识点总结

    python全栈知识点总结

    在本篇文章里小编给大家整理了关于python全栈的知识点以及学习路线的总结,需要的朋友们参考下。
    2019-07-07
  • 基于python实现在excel中读取与生成随机数写入excel中

    基于python实现在excel中读取与生成随机数写入excel中

    最近接个项目,项目要求是这样的:在一份已知的excel表格中读取学生的学号与姓名,再将这些数据放到新的excel表中的第一列与第二列,最后再生成随机数作为学生的考试成绩,具体实现代码大家参考下本文
    2018-01-01
  • Python 读取 .gz 文件全过程

    Python 读取 .gz 文件全过程

    这篇文章主要介绍了Python 读取 .gz 文件全过程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • 详解python3百度指数抓取实例

    详解python3百度指数抓取实例

    本篇文章主要介绍了python3百度指数抓取,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧。
    2016-12-12
  • python中Requests发送json格式的post请求方法

    python中Requests发送json格式的post请求方法

    这篇文章主要介绍了python中Requests发送json格式的post请求方法,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的朋友可以参考一下
    2022-09-09
  • Python中常用操作字符串的函数与方法总结

    Python中常用操作字符串的函数与方法总结

    这篇文章主要介绍了Python中常用操作字符串的函数与方法总结,包括字符串的格式化输出与拼接等基础知识,需要的朋友可以参考下
    2016-02-02

最新评论