文章目录
- [一. 多任务](#一. 多任务)
-
- [1. 多任务的概念](#1. 多任务的概念)
- [2. 多任务的两种表现形式](#2. 多任务的两种表现形式)
-
- [① 并发](#① 并发)
- [② 并行](#② 并行)
- [二. 进程](#二. 进程)
-
- [1. 进程的概念](#1. 进程的概念)
- [2. 多进程的作用](#2. 多进程的作用)
-
- [① 单进程执行](#① 单进程执行)
- [② 多进程进程](#② 多进程进程)
- [3. 多进程完成多任务](#3. 多进程完成多任务)
-
- [① 不带参数](#① 不带参数)
- [② 带参数](#② 带参数)
- [4. 获取进程编号](#4. 获取进程编号)
-
- [① 进程编号的作用](#① 进程编号的作用)
- [② 两种进程编号](#② 两种进程编号)
-
- [Ⅰ. 获取当前进程编号](#Ⅰ. 获取当前进程编号)
- [Ⅱ. 获取当前进程的父进程ppid=parent pid](#Ⅱ. 获取当前进程的父进程ppid=parent pid)
- [5. 进程应用注意点](#5. 进程应用注意点)
-
- [① 进程间不共享全局变量](#① 进程间不共享全局变量)
- [② 主进程与子进程的结束顺序](#② 主进程与子进程的结束顺序)
-
- [Ⅰ. 主进程结束,无法确保子进程也结束](#Ⅰ. 主进程结束,无法确保子进程也结束)
- [Ⅱ. 设置守护进程](#Ⅱ. 设置守护进程)
- [Ⅲ. 销毁子进程](#Ⅲ. 销毁子进程)
- [三. 线程](#三. 线程)
-
- [1. 线程的概念](#1. 线程的概念)
- [2. 多线程的作用](#2. 多线程的作用)
-
- [① 单线程执行](#① 单线程执行)
- [② 多线程执行](#② 多线程执行)
- [3. 多线程完成多任务](#3. 多线程完成多任务)
-
- [① 不带参数](#① 不带参数)
- [② 带参数](#② 带参数)
- [4. 获取线程编号](#4. 获取线程编号)
- [5. 线程应用注意点](#5. 线程应用注意点)
-
- [① 线程间共享全局变量](#① 线程间共享全局变量)
- [② 主线程与子线程的结束顺序](#② 主线程与子线程的结束顺序)
-
- [Ⅰ. 主线程结束,无法确保子线程也结束](#Ⅰ. 主线程结束,无法确保子线程也结束)
- [Ⅱ. 设置守护线程方式一](#Ⅱ. 设置守护线程方式一)
- [Ⅲ. 设置守护线程方式二](#Ⅲ. 设置守护线程方式二)
- [Ⅳ. 线程间的执行顺序](#Ⅳ. 线程间的执行顺序)
- [四. 进程和线程对比](#四. 进程和线程对比)
-
- [1. 关系对比](#1. 关系对比)
-
- [① 线程是依附在进程里面的,没有进程就没有线程。](#① 线程是依附在进程里面的,没有进程就没有线程。)
- [② 一个进程默认提供一条线程,进程可以创建多个线程。](#② 一个进程默认提供一条线程,进程可以创建多个线程。)
- [2. 区别对比](#2. 区别对比)
-
- [① 进程之间不共享全局变量](#① 进程之间不共享全局变量)
- [② 线程之间共享全局变量](#② 线程之间共享全局变量)
- [③ 创建进程的资源开销要比创建线程的资源开销要大](#③ 创建进程的资源开销要比创建线程的资源开销要大)
- [④ 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位](#④ 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位)
- [3. 优缺点对比](#3. 优缺点对比)
-
- [① 进程优缺点:](#① 进程优缺点:)
- [② 线程优缺点](#② 线程优缺点)
一. 多任务
1. 多任务的概念
多任务是指在同一时间内执行多个任务
多任务的最大好处是充分利用CPU资源,提高程序的执行效率
2. 多任务的两种表现形式
① 并发
并发:在一段时间内交替去执行多个任务。
例如:对于单核cpu处理多任务,操作系统轮流让各个任务交替执行,假如:软件1执行0.01秒,切换到软件2,软件2执行0.01秒,再切换到软件3,执行0.01秒......这样反复执行下去, 实际上每个软件都是交替执行的 . 但是,由于CPU的执行速度实在是太快了,表面上我们感觉就像这些软件都在同时执行一样 . 这里需要注意单核cpu是并发的执行多任务的
② 并行
并行:在一段时间内真正的同时一起执行多个任务。
对于多核cpu处理多任务,操作系统会给cpu的每个内核安排一个执行的任务,多个内核是真正的一起同时执行多个任务。这里需要注意多核cpu是并行的执行多任务,始终有多个任务一起执行。
二. 进程
在Python中,想要实现多任务可以使用多进程来完成。
1. 进程的概念
进程(Process)是资源分配的最小单位,它是操作系统进行资源分配和调度运行的基本单位,通俗理解:一个正在运行的程序就是一个进程。
例如:正在运行的qq , 微信等 他们都是一个进程。注: 一个程序运行后至少有一个进程
2. 多进程的作用
① 单进程执行
python
def func_a():
print('任务A')
def func_b():
print('任务B')
func_a()
func_b()
图中是一个非常简单的程序 , 一旦运行hello.py这个程序 , 按照代码的执行顺序 , func_a函数执行完毕后才能执行func_b函数 . 如果可以让func_a和func_b同时运行 , 显然执行hello.py这个程序的效率会大大提升
② 多进程进程
3. 多进程完成多任务
① 不带参数
python
① 导入进程包
import multiprocessing
② 通过进程类创建进程对象
进程对象 = multiprocessing.Process()
③ 启动进程执行任务
进程对象.start()
进程对象 = multiprocessing.Process([group [, target=任务名 [, name]]])
参数名 | 说明 |
---|---|
target | 执行的目标任务名,这里指的是函数名(方法名) |
name | 进程名,一般不用设置 |
group | 进程组,目前只能使用None |
代码:
python
#导入进程包
import multiprocessing
import time
def music():
for i in range(3):
print("听音乐")
time.sleep(0.2)
def coding():
for i in range(3):
print("敲打吗")
time.sleep(0.2)
if __name__ == '__main__':
#通过进程类创建进程对象
music_process = multiprocessing.Process(target=music)
coding_process = multiprocessing.Process(target=coding)
#启动进程执行任务
music_process.start()
coding_process.start()
② 带参数
Process([group [, target [, name [, args [, kwargs]]]]])
参数名 | 说明 |
---|---|
args | 以元组的方式给执行任务传参,args表示调用对象的位置参数元组,args=(1,2,'anne',) |
kwargs | 以字典方式给执行任务传参,kwargs表示调用对象的字典,kwargs={'name':'anne','age':18} |
单参数:元组单元素,逗号不能省
python
# 导入进程包
import multiprocessing
import time
def music(num):
for i in range(num):
print("听音乐")
time.sleep(0.2)
def coding(count):
for i in range(count):
print("敲代码")
time.sleep(0.2)
if __name__ == '__main__':
# 通过进程类创建进程对象
music_process = multiprocessing.Process(target=music, args=(3,))
coding_process = multiprocessing.Process(target=coding, kwargs={"count": 3})
# 启动进程执行任务
music_process.start()
coding_process.start()
多参数:
python
# 导入进程包
import multiprocessing
import time
def music(num,name):
print(name)
for i in range(num):
print("听音乐")
time.sleep(0.2)
def coding(count,name):
print(name)
for i in range(count):
print("敲代码")
time.sleep(0.2)
if __name__ == '__main__':
# 通过进程类创建进程对象
music_process = multiprocessing.Process(target=music, args=(3,"多任务开始1"))
coding_process = multiprocessing.Process(target=coding, kwargs={"count": 3,"name":"多任务开始2"})
# 启动进程执行任务
music_process.start()
coding_process.start()
4. 获取进程编号
① 进程编号的作用
当程序中进程的数量越来越多时 , 如果没有办法区分主进程和子进程还有不同的子进程 , 那么就无法进行有效的进程管理 , 为了方便管理实际上每个进程都是有自己编号的。
② 两种进程编号
Ⅰ. 获取当前进程编号
python
getpid()
Ⅱ. 获取当前进程的父进程ppid=parent pid
python
getppid()
python
import multiprocessing
import time
import os
def music(num):
print('music>> %d' % os.getpid())
print('music主进程>> %d' % os.getppid())
for i in range(num):
print('听音乐...')
time.sleep(0.2)
def coding(count):
print('coding>> %d' % os.getpid())
print('coding主进程>> %d' % os.getppid())
for i in range(count):
print('敲代码...')
time.sleep(0.2)
if __name__ == '__main__':
print('主进程>> %d' % os.getpid())
music_process = multiprocessing.Process(target=music, args=(3, ))
coding_process = multiprocessing.Process(target=coding, kwargs={'count': 3})
music_process.start()
coding_process.start()
5. 进程应用注意点
① 进程间不共享全局变量
实际上创建一个子进程就是把主进程的资源进行拷贝产生了一个新的进程,这里的主进程和子进程是互相独立的。
python
import multiprocessing
import time
my_list = []
def write_data():
for i in range(3):
my_list.append(i)
print('add:', i)
print(my_list)
def read_data():
print('read_data', my_list)
if __name__ == '__main__':
# 创建写入数据进程
write_process = multiprocessing.Process(target=write_data)
# 创建读取数据进程
read_process = multiprocessing.Process(target=read_data)
# 启动进程执行相关任务
write_process.start()
time.sleep(1)
read_process.start()
print('main_data', my_list)
add: 0
add: 1
add: 2
[0, 1, 2]
main_data []
read_data []
原理分析:
三个进程分别操作的都是自己进程里面的全局变量my_list, 不会对其它进程里面的全局变量产生影响,所以进程之间不共享全局变量,只不过进程之间的全局变量名字相同而已,但是操作的不是同一个进程里面的全局变量
创建子进程会对主进程资源进行拷贝,也就是说子进程是主进程的一个副本,好比是一对双胞胎,之所以进程之间不共享全局变量,是因为操作的不是同一个进程里面的全局变量,只不过不同进程里面的全局变量名字相同而已。
② 主进程与子进程的结束顺序
Ⅰ. 主进程结束,无法确保子进程也结束
python
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()
# 延迟1s
time.sleep(1)
print('主进程执行完毕')
Ⅱ. 设置守护进程
python
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.daemon = True
# 启动子进程
work_process.start()
# 延迟1s
time.sleep(1)
print('主进程执行完毕')
Ⅲ. 销毁子进程
python
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()
# 延迟1s
time.sleep(1)
# 让子进程直接销毁,表示终止执行, 主进程退出之前,把所有的子进程直接销毁就可以了
work_process.terminate()
print('主进程执行完毕')
以上两种方式都能保证主进程退出子进程销毁
三. 线程
在Python中,想要实现多任务还可以使用多线程来完成。
1. 线程的概念
进程是分配资源的最小单位 , 一旦创建一个进程就会分配一定的资源 , 就像跟两个人聊QQ就需要打开两个QQ软件一样是比较浪费资源的 .
线程是程序执行的最小单位 , 实际上进程只负责分配资源 , 而利用这些资源执行程序的是线程 , 也就说进程是线程的容器 , 一个进程中最少有一个线程来负责执行程序 。同时线程自己不拥有系统资源,只需要一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源 。这就像通过一个QQ软件(一个进程)打开两个窗口(两个线程)跟两个人聊天一样 , 实现多任务的同时也节省了资源。
2. 多线程的作用
python
def func_a():
print('任务A')
def func_b():
print('任务B')
func_a()
func_b()
① 单线程执行
② 多线程执行
3. 多线程完成多任务
① 不带参数
python
① 导入线程模块
import threading
② 通过线程类创建线程对象
线程对象 = threading.Thread(target=任务名)
② 启动线程执行任务
线程对象.start()
参数名 | 说明 |
---|---|
target | 执行的目标任务名,这里指的是函数名(方法名) |
name | 线程名,一般不用设置 |
group | 线程组,目前只能使用None |
python
import threading
import time
def music():
for i in range(3):
print('听音乐...')
time.sleep(0.2)
def coding():
for i in range(3):
print('敲代码...')
time.sleep(0.2)
if __name__ == '__main__':
music_thread = threading.Thread(target=music)
coding_thread = threading.Thread(target=coding)
music_thread.start()
coding_thread.start()
② 带参数
参数名 | 说明 |
---|---|
args | 以元组的方式给执行任务传参 |
kwargs | 以字典方式给执行任务传参 |
python
import threading
import time
def music(num):
for i in range(num):
print('听音乐...')
time.sleep(0.2)
def coding(count):
for i in range(count):
print('敲代码...')
time.sleep(0.2)
if __name__ == '__main__':
music_thread = threading.Thread(target=music, args=(3,))
coding_thread = threading.Thread(target=coding, kwargs={"count": 3})
music_thread.start()
coding_thread.start()
4. 获取线程编号
python
import time
import threading
def music(num):
print(f'music>> {threading.current_thread()}')
for i in range(num):
print('听音乐...')
time.sleep(0.2)
def coding(count):
print(f'coding>> {threading.current_thread()}')
for i in range(count):
print('敲代码...')
time.sleep(0.2)
if __name__ == '__main__':
print(f'main>> {threading.current_thread()}')
music_thread = threading.Thread(target=music, args=(3, ))
coding_thread = threading.Thread(target=coding, kwargs={'count': 3})
music_thread.start()
coding_thread.start()
5. 线程应用注意点
① 线程间共享全局变量
多个线程都是在同一个进程中 , 多个线程使用的资源都是同一个进程中的资源 ,因此多线程间是共享全局变量
python
import threading
import time
my_list = []
def write_data():
for i in range(3):
print('add:', i)
my_list.append(i)
print(my_list)
def read_data():
print('read:', 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()
add: 0
add: 1
add: 2
[0, 1, 2]
read: [0, 1, 2]
② 主线程与子线程的结束顺序
Ⅰ. 主线程结束,无法确保子线程也结束
python
import time
import threading
def work():
for i in range(10):
print('work...')
time.sleep(0.2)
if __name__ == '__main__':
# 创建子进程
work_thread = threading.Thread(target=work)
# 启动线程
work_thread.start()
# 延时1s
time.sleep(1)
print('主线程执行完毕')
Ⅱ. 设置守护线程方式一
python
import time
import threading
def work():
for i in range(10):
print('work...')
time.sleep(0.2)
if __name__ == '__main__':
# 创建子线程并设置守护主线程
work_thread = threading.Thread(target=work, daemon=True)
# 启动线程
work_thread.start()
# 延时1s
time.sleep(1)
print('主线程执行完毕')
Ⅲ. 设置守护线程方式二
python
import time
import threading
def work():
for i in range(10):
print('work...')
time.sleep(0.2)
if __name__ == '__main__':
# 创建子进程
work_thread = threading.Thread(target=work)
# 设置守护主线程
work_thread.setDaemon(True)
# 启动线程
work_thread.start()
# 延时1s
time.sleep(1)
print('主线程执行完毕')
Ⅳ. 线程间的执行顺序
线程之间执行是无序的,是由CPU调度决定某个线程先执行的。
python
import threading
import time
def get_info():
# 可以暂时先不加,查看效果
time.sleep(0.5)
current_thread = threading.current_thread()
print(current_thread)
if __name__ == '__main__':
# 创建子线程
for i in range(10):
sub_thread = threading.Thread(target=get_info)
sub_thread.start()
四. 进程和线程对比
1. 关系对比
① 线程是依附在进程里面的,没有进程就没有线程。
② 一个进程默认提供一条线程,进程可以创建多个线程。
2. 区别对比
① 进程之间不共享全局变量
② 线程之间共享全局变量
③ 创建进程的资源开销要比创建线程的资源开销要大
④ 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
3. 优缺点对比
① 进程优缺点:
优点:可以用多核
缺点:资源开销大
② 线程优缺点
优点:资源开销小
缺点:不能使用多核