Python进程和线程
一、多任务的概念
1、什么是多任务?
多任务是指在同一时间内执行多个任务。
例如:现在电脑安装的操作系统都是多任务操作系统,可以同时运行着多个软件。
2、多任务的两种表现形式
- 并发
- 并行
3、并发操作
并发:在一段时间内交替去执行多个任务。
例如:对于单核cpu处理多任务,操作系统轮流让各个任务交替执行,假如:软件1执行0.01秒,切换到软件2,软件2执行0.01秒,再切换到软件3,执行0.01秒……这样反复执行下去
, 实际上每个软件都是交替执行的 . 但是,由于CPU的执行速度实在是太快了,表面上我们感觉就像这些软件都在同时执行一样 . 这里需要注意单核cpu是并发的执行多任务的。
4、并行操作
并行:在一段时间内真正的同时一起执行多个任务。
对于多核cpu处理多任务,操作系统会给cpu的每个内核安排一个执行的任务,多个内核是真正的一起同时执行多个任务。这里需要注意多核cpu是并行的执行多任务,始终有多个任务一起执行。
二、进程的概念
1、程序中实现多个任务的方式
在Python中,想要实现多任务可以使用多进程来完成。
2、进程的概念
进程(Process)是资源分配的最小单位,它是操作系统进行资源分配和调度运行的基本单位,通俗理解:一个正在运行的程序就是一个进程。
例如:正在运行的QQ,微信等它们都是一个进程。
注意:一个程序运行后至少右一个进程
3、多进程的作用
- 未使用多进程
思考:图中是一个非常简单的程序,一旦运行这个程序,按照代码的执行顺序,func_A函数执行完毕后才会执行func_B这个函数,如果可以让func_A和func_B同时运行,显然执行这个程序的效率会大大提升。
- 使用了多进程
三、多进程完成多任务
1、多进程完成多任务
# 导入进包
import multiprocessing
# 通过进程类创建进程对象
进程对象 = multiprocessing.Process()
# 启动进程执行任务
进程对象.start()
2、通过进程类创建进程对象
进程对象 = multiprocessing.Process([group [, target=任务名 [, name]]])
参数说明:
参数名 | 说明 |
---|---|
target | 执行的目标任务名,这里指的是函数名(方法名) |
name | 进程名,一般不用设置 |
group | 进程组,目前只能使用None |
3、进程创建与启动代码
边听音乐边敲代码:
import multiprocessing
import time
# 边听音乐边敲代码
def music():
for i in range(5):
print('听音乐。。。')
time.sleep(0.3)
def coding():
for i in range(5):
print('敲代码。。。')
time.sleep(0.3)
if __name__ == '__main__':
music_process = multiprocessing.Process(target=music)
coding_process = multiprocessing.Process(target=coding)
music_process.start()
coding_process.start()
4、进程执行带有参数的任务
Process([group [, target [, name [, args [, kwargs]]]]])
参数说明:
参数名 | 说明 |
---|---|
args | 以元组的方式给执行任务传参,args表示调用对象的位置参数元组,args=(1,2,‘anne’) |
kwargs | 以字典方式给执行任务参数,kwargs表示调用对象的字典,kwargs={‘name’:‘anne’,‘age’:18} |
案例:args参数和kwargs参数的使用
import multiprocessing
import time
# 边听音乐边敲代码
def music(num):
for i in range(num):
print('听音乐。。。')
time.sleep(0.3)
def coding(num):
for i in range(num):
print('敲代码。。。')
time.sleep(0.3)
if __name__ == '__main__':
music_process = multiprocessing.Process(target=music, args=(10,))
coding_process = multiprocessing.Process(target=coding, kwargs={'num': 10})
music_process.start()
coding_process.start()
案例:多个参数传递
import multiprocessing
import time
# 边听音乐边敲代码
def music(name, num):
for i in range(num):
print(f'{name}听音乐。。。')
time.sleep(0.3)
def coding(name, num):
for i in range(num):
print(f'{name}敲代码。。。')
time.sleep(0.3)
if __name__ == '__main__':
music_process = multiprocessing.Process(target=music, args=('小明', 10))
coding_process = multiprocessing.Process(target=coding, kwargs={'name': '小明', 'num': 10})
music_process.start()
coding_process.start()
四、获取进程编号
1、进程编号的作用
当程序中进程的数量越来越多时,如果没有办法区分主进程和子进程还有不同的子进程,那么就无法进行有效的进程管理,为了方便管理实际上每个进程都是有自己编程的。
2、两种进程编号
- 获取当前进程编号
getpid()
- 获取当前进程的父进程ppid = parent pid
getppid()
3、获取当前进程编号
import os
def func():
# 获取当前进程编号
print('当前进程编号:', os.getpid())
# 获取父进程编号
print('父进程编号', os.getppid())
func()
案例:获取子进程编号
import multiprocessing
import time
import os
# 边听音乐边敲代码
def music(name, num):
for i in range(num):
print('music-->子进程编号:', os.getpid())
print(f'{name}听音乐。。。')
time.sleep(0.3)
def coding(name, num):
for i in range(num):
print('coding-->子进程编号:', os.getpid())
print(f'{name}敲代码。。。')
time.sleep(0.3)
if __name__ == '__main__':
music_process = multiprocessing.Process(target=music, args=('小明', 10))
coding_process = multiprocessing.Process(target=coding, kwargs={'name': '小明', 'num': 10})
music_process.start()
coding_process.start()
案例:获取父进程编号
import multiprocessing
import time
import os
# 边听音乐边敲代码
def music(name, num):
for i in range(num):
print('music-->父进程编号:', os.getppid())
print(f'{name}听音乐。。。')
time.sleep(0.3)
def coding(name, num):
for i in range(num):
print('coding-->父进程编号:', os.getppid())
print(f'{name}敲代码。。。')
time.sleep(0.3)
if __name__ == '__main__':
music_process = multiprocessing.Process(target=music, args=('小明', 10))
coding_process = multiprocessing.Process(target=coding, kwargs={'name': '小明', 'num': 10})
music_process.start()
coding_process.start()
五、进程应用注意点
1、进程间不共享全局变量
实际上创建一个子进程就是把主进程的资源进行拷贝产生了一个新的进程,这里的主进程和子进程是相互独立的。
案例:
import multiprocessing
import time
my_list = []
def write_data():
for i in range(3):
my_list.append(i)
print(my_list)
def read_data():
print('read_data-->', my_list)
if __name__ == '__main__':
write_data_process = multiprocessing.Process(target=write_data)
read_data_process = multiprocessing.Process(target=read_data)
write_data_process.start()
time.sleep(1)
read_data_process.start()
'''
[0, 1, 2]
read_data--> []
'''
原理分析:
三个进程分别操作的都是自己进程里面的全局变量my_list, 不会对其它进程里面的全局变量产生影响,所以进程之间不共享全局变量,只不过进程之间的全局变量名字相同而已,但是操作的不是同一个进程里面的全局变量。
2、主进程与子进程的结束顺序
代码演示:
import multiprocessing
import time
def work():
for i in range(10):
print('正在敲代码...')
time.sleep(0.2)
if __name__ == '__main__':
work_process = multiprocessing.Process(target=work)
work_process.start()
time.sleep(1)
print('主进程结束')
- 解决方案一:设置守护进程
import multiprocessing
import time
def work():
for i in range(10):
print('正在敲代码...')
time.sleep(0.2)
if __name__ == '__main__':
# daemon=True设置守护进程
work_process = multiprocessing.Process(target=work, daemon=True)
work_process.start()
time.sleep(1)
print('主进程结束')
- 解决方案二:销毁子进程
import multiprocessing
import time
def work():
for i in range(10):
print('正在敲代码...')
time.sleep(0.2)
if __name__ == '__main__':
work_process = multiprocessing.Process(target=work)
work_process.start()
time.sleep(1)
# 让子进程直接销毁,表示终止执行, 主进程退出前,把所有子进程直接销毁就可以了
work_process.terminate()
print('主进程结束')
提示:以上两种方式都能保证主进程退出子进程销毁
六、线程的概念
1、线程的概念
在Python中想要实现多任务还可以使用多线程来完成
2、为什么使用多线程?
进程是分配资源的最小单位 , 一旦创建一个进程就会分配一定的资源 , 就像跟两个人聊QQ就需要打开两个QQ软件一样是比较浪费资源的 .
线程是程序执行的最小单位 , 实际上进程只负责分配资源 , 而利用这些资源执行程序的是线程 , 也就说进程是线程的容器 , 一个进程中最少有一个线程来负责执行程序 。同时线程自己不拥有系统资源,只需要一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。这就像通过一个QQ软件(一个进程)打开两个窗口(两个线程)跟两个人聊天一样 , 实现多任务的同时也节省了资源。
3、多线程的作用
def func_A():
print('任务A')
def func_B():
print('任务B')
func_A()
func_B()
- 单线程执行
- 多线程执行
八、多线程完成多任务
1、多线程完成多任务
# 导入线程模块
import threading
# 通过线程类创建线程对象
线程对象 = treading.Thread(target=任务名)
# 启动线程执行任务
线程对象.start()
参数名 | 说明 |
---|---|
target | 执行的目标任务后,这里指的是函数名(方法名) |
name | 线程名,一般不用设置 |
group | 线程组,目前只能使用None |
2、线程创建与启动
单线程案例:
import time
# 边听音乐边敲代码
def music():
for i in range(5):
print('听音乐。。。')
time.sleep(0.3)
def coding():
for i in range(5):
print('敲代码。。。')
time.sleep(0.3)
if __name__ == '__main__':
music()
coding()
多线程案例:
import time
import threading
# 边听音乐边敲代码
def music():
for i in range(5):
print('听音乐。。。')
time.sleep(0.3)
def coding():
for i in range(5):
print('敲代码。。。')
time.sleep(0.3)
if __name__ == '__main__':
music_thread = threading.Thread(target=music)
coding_thread = threading.Thread(target=coding)
music_thread.start()
coding_thread.start()
3、线程执行带有参数的任务
参数名 | 说明 |
---|---|
args | 以元组的方式给执行任务参数 |
kwargs | 以字典方式给执行任务传参 |
import time
import threading
# 边听音乐边敲代码
def music(num):
for i in range(num):
print('听音乐。。。')
time.sleep(0.3)
def coding(num):
for i in range(num):
print('敲代码。。。')
time.sleep(0.3)
if __name__ == '__main__':
music_thread = threading.Thread(target=music, args=(5,))
coding_thread = threading.Thread(target=coding, kwargs={'num': 5})
music_thread.start()
coding_thread.start()
4、主线程和子线程的结束顺序
import time
import threading
# 边听音乐边敲代码
def music():
for i in range(5):
print('听音乐。。。')
time.sleep(0.4)
if __name__ == '__main__':
music_thread = threading.Thread(target=music)
music_thread.start()
time.sleep(1)
print('主进程结束')
- 设置守护线程方式一:
import time
import threading
# 边听音乐边敲代码
def music():
for i in range(5):
print('听音乐。。。')
time.sleep(0.4)
if __name__ == '__main__':
music_thread = threading.Thread(target=music, daemon=True)
music_thread.start()
time.sleep(1)
print('主进程结束')
- 设置守护进程方式二:
import time
import threading
# 边听音乐边敲代码
def music():
for i in range(5):
print('听音乐。。。')
time.sleep(0.4)
if __name__ == '__main__':
music_thread = threading.Thread(target=music)
music_thread.daemon = True
music_thread.start()
time.sleep(1)
print('主进程结束')
5、线程间的执行顺序
for i in range(5):
sub_thread = threading.Thread(target=task)
sub_thread.start()
线程间的执行是无序的
验证:
- 获取当前线程信息
# 通过current_thread方法获取线程对象
current_thread = threading.current_thread()
# 通过current_thread对象可以知道线程的相关信息,例如被创建的顺序
print(current_thread)
- 线程间的执行顺序
import time
import threading
def get_info():
time.sleep(0.5)
current_thread = threading.current_thread()
print(current_thread)
if __name__ == '__main__':
for i in range(10):
get_info_thread = threading.Thread(target=get_info)
get_info_thread.start()
总结:线程间执行是无序的,是由CPU调度决定某个线程先执行的。
6、线程间共享全局变量
- 线程间共享全局变量
多个线程都是在同一个进程中,多个线程使用的资源都是同一个进程中的资源,因此多线程间是共享全局变量
示例:
import time
import threading
my_list = []
def write_data():
for i in range(5):
my_list.append(i)
print('write_data-->', my_list)
def read_data():
print('read_data-->', my_list)
if __name__ == '__main__':
write_thread = threading.Thread(target=write_data)
read_thread = threading.Thread(target=read_data)
write_thread.start()
time.sleep(1)
read_thread.start()
7、总结:进程和线程对比
- 关系对比
1.线程是依附在进程里面的,没有进程就没有线程。
2.一个进程默认提供一条线程,进程可以创建多个线程。
- 区别对比
1.进程之间不共享全局变量;线程之间共享全局变量
2.创建进程的资源开销要不创建线程的资源开销要大
3.进程是操作系统资源分配的基本单元,线程是CPU调度的基本单元
- 优缺点对比
1.进程优缺点:
优点:可以使用多核
缺点:资源开销大
2.线程优缺点:
优点:资源开销小
一个进程中的资源,因此多线程间是共享全局变量
示例:
import time
import threading
my_list = []
def write_data():
for i in range(5):
my_list.append(i)
print('write_data-->', my_list)
def read_data():
print('read_data-->', my_list)
if __name__ == '__main__':
write_thread = threading.Thread(target=write_data)
read_thread = threading.Thread(target=read_data)
write_thread.start()
time.sleep(1)
read_thread.start()
7、总结:进程和线程对比
- 关系对比
1.线程是依附在进程里面的,没有进程就没有线程。
2.一个进程默认提供一条线程,进程可以创建多个线程。
- 区别对比
1.进程之间不共享全局变量;线程之间共享全局变量
2.创建进程的资源开销要不创建线程的资源开销要大
3.进程是操作系统资源分配的基本单元,线程是CPU调度的基本单元
- 优缺点对比
1.进程优缺点:
优点:可以使用多核
缺点:资源开销大
2.线程优缺点:
优点:资源开销小
缺点:不能使用多核