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
和一个锁对象lock
。worker
函数是每个线程的工作内容,它通过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实现在excel中读取与生成随机数写入excel中
最近接个项目,项目要求是这样的:在一份已知的excel表格中读取学生的学号与姓名,再将这些数据放到新的excel表中的第一列与第二列,最后再生成随机数作为学生的考试成绩,具体实现代码大家参考下本文2018-01-01python中Requests发送json格式的post请求方法
这篇文章主要介绍了python中Requests发送json格式的post请求方法,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的朋友可以参考一下2022-09-09
最新评论