Python多线程编程
1 进程
1.1 多任务的介绍
- 多任务是指在同一时间内执行多个任务
- 多任务的最大好处是充分利用CPU资源提高程序的执行效率
- 多任务的两种表现形式
- 并发
在一段时间内交替去执行多个任务 - 并行
在一段时间内真正的同时一起执行多个任务
1.2 进程的介绍
在Python中,想要实现多任务可以使用多进程来完成
- 进程(Process)是资源分配的最小单位,它是操作系统进行资源分配和调度运行的基本单位,通俗理解:一个正在运行的程序就是一个进程,例如:正在运行的qq,微信等他们都是一个进程
- 程序运行会默认创建一个进程,这个默认创建的进程我们称之为主进程
- 程序运行后又创建了一个进程这个新创建的进程我们称之为子进程
1.3 多进程完成多任务
1.3.1 进程的创建步骤
- 导入进程包
import multiprocessing
- 通过进程类创建进程对象
进程对象 = multiprocessing.Process(target=任务名)
参数名 | 说明 |
---|
target | 执行的目标任务名,这里指的是函数名(方法名) | name | 进程名,一般不用设置 | group | 进程组 | args | 以元组的方式给执行任务传参,需保证顺序和方法的参数顺序一致 | kwargs | 以字典的方式给执行任务传参,需保证key和方法的参数名一致 |
- 启动进程执行任务
进程对象.start()
1.3.2 进程创建与启动的代码
import multiprocessing
import time
def sing(num):
for i in range(num):
print('唱歌...')
time.sleep(0.5)
def dance(num):
for i in range(num):
print('跳舞...')
time.sleep(0.5)
if __name__ == '__main__':
sing_process = multiprocessing.Process(target=sing, args=(3,))
dance_process = multiprocessing.Process(target=dance, kwargs={'num': 3})
sing_process.start()
dance_process.start()
====================分隔线====================
运行结果:
跳舞...
唱歌...
跳舞...
唱歌...
跳舞...
唱歌...
1.4 获取进程编号
当程序中进程的数量越来越多时,如果没有办法区分主进程和子进程还有不同的子进程,那么就无法进行有效的进程管理,为了方便管理实际上每个进程都是有自己编号的
- 获取当前进程编号
os.getpid() - 获取当前父进程编号
os.getppid()
1.5 进程的注意点
- 主进程会等待所有的子进程执行结束再结束
我们可以使用代码来验证这点:
import multiprocessing
import time
def work():
for i in range(4):
print('工作中...')
time.sleep(0.5)
if __name__ == '__main__':
work_process = multiprocessing.Process(target=work)
work_process.start()
time.sleep(1)
print('主进程执行完成...')
====================分隔线====================
运行结果:
工作中...
工作中...
主进程执行完成...
工作中...
工作中...
- 设置守护主进程
work_process.daemon = True
我们在work_process.start() 的上一行,增加这行代码的运行结果:
工作中...
工作中...
主进程执行完成...
这一次主进程结束,子进程也跟着一并销毁了,不再执行子进程的代码 3. 知识要点 为了保证子进程能够正常的运行,主进程会等所有的子进程执行完成以后再销毁,设置守护主进程的目的是主进程退出子进程销毁,不让主进程再等待子进程去执行
1.6 动手实践
实现一个高并发copy器
需求分析:
- 目标文件夹是否存在,如果不存在就创建,如果存在则不创建
- 遍历源文件夹中所有文件,并拷贝到目标文件夹
- 采用进程实现多任务,完成高并发拷贝
代码实现:
import multiprocessing
import os
def copy_file(file_name, source_dir, dest_dir):
source_path = os.path.join(source_dir, file_name)
dest_path = os.path.join(dest_dir, file_name)
with open(source_path, 'rb') as source_file:
with open(dest_path, 'wb') as dest_file:
while True:
data = source_file.read(1024)
if data:
dest_file.write(data)
else:
break
if __name__ == '__main__':
source_dir_ = r'D:\Code\Driver'
dest_dir_ = r'C:\Users\Albus_Rowe\Desktop\Test'
try:
os.mkdir(dest_dir_)
except FileExistsError:
print('目标文件夹已经存在')
file_list = os.listdir(source_dir_)
for file_name_ in file_list:
sub_process = multiprocessing.Process(target=copy_file, args=(file_name_, source_dir_, dest_dir_))
sub_process.start()
2 线程
2.1 线程的介绍
在Python中,想要实现多任务还可以使用多线程来完成 进程是分配资源的最小单位,一旦创建一个进程就会分配一定的资源,就像跟两个人聊QQ就需要打开两个QQ软件一样是比较浪费资源的 线程是程序执行的最小单位,实际上进程只负责分配资源,而利用这些资源执行程序的是线程,也就说进程是线程的容器,一个进程中最少有一个线程来负责执行程序,同时线程自己不拥有系统资源,只需要一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源,这就像通过一个QQ软件(一个进程)打开两个窗口(两个线程)跟两个人聊天一样,实现多任务的同时也节省了资源
2.2 多线程完成多任务
线程的创建步骤:
- 导入线程模块
import threading - 通过线程类创建线程对象
线程对象 = threading.Thread(target=任务名) - 启动线程执行任务
线程对象.start()
其余内容和进程没多大区别,这里只介绍有差异的地方
- 设置守护主线程
work_thread = threading.Thread(target=work, args=(3,), daemon=True) 或者 work_thread.setDaemon(True)
2.3 线程之间执行是无序的
我们可以使用代码来验证这点:
import threading
import time
def task():
time.sleep(0.01)
thread = threading.current_thread()
print(thread)
if __name__ == '__main__':
for i in range(5):
sub_thread = threading.Thread(target=task)
sub_thread.start()
====================分隔线====================
运行结果:
<Thread(Thread-4, started 7680)>
<Thread(Thread-3, started 13676)>
<Thread(Thread-1, started 1780)>
<Thread(Thread-5, started 12144)>
<Thread(Thread-2, started 3136)>
线程的执行顺序和创建顺序无关
2.4 进程和线程对比
2.4.1 关系对比
- 线程是依附在进程里面的,没有进程就没有线程
- 一个进程默认提供一条线程,进程可以创建多个线程
2.4.2 区别对比
- 创建进程的资源开销要比创建线程的资源开销要大
- 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
- 线程不能够独立执行,必须依存在进程中
2.4.3 优缺点对比
- 进程优缺点
- 线程优缺点
|