1. 并行
多核CPU处理多任务,操作系统会给cpu的每个内核安排一个执行的软件,多个内核是真正的一起执行软件。这里需要注意多核cpu是并行的执行多任务,始终有多个软件一起执行。
1.1 进程的概念
一个正在运行的程序或者软件就是一个进程,它是操作系统进行资源分配的基本单位,也就是说每启动一个进程,操作系统都会给其分配一定的运行资源(内存资源)保证进程的运行。
注意:
一个程序运行后至少有一个进程,一个进程默认有一个线程,进程里面可以创建多个线程,线程是依附在进程里面的,没有进程就没有线程。
1.2 进程的使用
1.2.1 导包
import multiprocessing
1.2.2 Process类
Process([group [, target [, name [, args [, kwargs]]]]])
group:指定进程组,目前只能使用None
target:执行的目标任务名
name:进程名字 args:以元组方式给执行任务传参
kwargs:以字典方式给执行任务传参
Process创建的实例对象的常用方法:
start():启动子进程实例(创建子进程)
join():等待子进程执行结束
terminate():不管任务是否完成,立即终止子进程
Process创建的实例对象的常用属性:
name:当前进程的别名,默认为Process-N,N为从1开始递增的整数
daemon:当前进程是否是守护线程
1.2.3 获取当前进程
获取当前进程对象
multiprocessing.current_process() 对象内有此进程id,name,是否是守护进程等属性
获取当前进程编号
os.getpid() 表示获取当前进程编号
multiprocessing.current_process().pid
获取当前父进程编号
os.getppid() 表示获取当前父进程编号
1.2.4 进程任务传参
Process类执行任务并给任务传参数有两种方式:
args 表示以元组的方式给执行任务传参
kwargs 表示以字典方式给执行任务传参
1.2.5 注意点
进程之间不共享全局变量
主进程会等待所有的子进程执行结束再结束
1.2.3 简单使用
# 1.导包
import multiprocessing
import os
def my_process(name):
"""2.定义一个任务"""
print(f"This is my process method,It`s name is {name}")
print(f'The process`s info is {multiprocessing.current_process()}')
print(f'The process`s id is {os.getpid()} / {multiprocessing.current_process().pid}')
print(f'The process`s parent id is {os.getppid()}')
if __name__ == '__main__':
# 创建任务
p = multiprocessing.Process(group=None,
target=my_process,
name="my_process",
kwargs={'name': 'p1'})
# 开启
p.start()
2. 并发
单核CPU,操作系统轮流让各个软件交替执行
2.1 线程的概念
线程是进程中执行代码的一个分支,每个执行分支(线程)要想工作执行代码需要cpu进行调度 ,也就是说线程是cpu调度的基本单位,每个进程至少都有一个线程,而这个线程就是我们通常说的主线程。
2.2 线程的使用
2.2.1 导包
import threading
2.2.2 Thead类
线程类Thread说明
Thead类的属性和方法和Process一致
这些属性和方法的用法、参数传递方式也和进程类Process一致
获取当前线程对象
threading.current_thread()
2.2.3 简单使用
# 1.导包
import threading
def my_thead(name):
# 2. 定义线程任务
print(f"this is my thead method,It`s name is {threading.current_thread()}")
print(f'The thead method name is {name}')
if __name__ == '__main__':
# 3.创建线程
t = threading.Thread(group=None,
target=my_thead,
name='my_first_test_thead',
kwargs={'name':'mt'})
# t.daemon = True # t.setDaemon(True)
t.start()
2.2.4 注意点
线程之间执行是无序的
主线程会等待所有的子线程执行结束再结束
线程之间共享全局变量
线程之间共享全局变量数据出现错误问题
2.2.5 线程锁-互斥锁
概念:
对共享数据进行锁定,保证同一时刻只能有一个线程去操作
注意:
互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程需要等待,等互斥锁使用完释放后,其它等待的线程再去抢这个锁
使用
threading模块中定义了Lock变量,这个变量本质上是一个函数,通过调用这个函数可以获取一把互斥锁。
# 4.创建锁
lock = threading.Lock()
def my_thead(name):
# 5. 上锁
lock.acquire()
print(f"this is my thead method,It`s name is {threading.current_thread()}")
print(f'The thead method name is {name}')
# 6. 释放锁
lock.release()
注意点:
acquire和release方法之间的代码同一时刻只能有一个线程去操作
如果在调用acquire方法的时候 其他线程已经使用了这个互斥锁,那么此时acquire方法会堵塞,直到这个互斥锁释放后才能再次上锁。
# 1.导包
import threading
# 定义全局变量
g_num = 0
# 4.创建锁
lock = threading.Lock()
def sum():
# 5. 上锁
lock.acquire()
# 2. 累加
for i in range(10000):
global g_num # 申明全局变量
print(f'{threading.current_thread().name} excuse add one to g_num')
g_num += 1
else:
print(f'{threading.current_thread().name} add result is {g_num}')
# 6. 释放锁
lock.release()
if __name__ == '__main__':
# 3.创建线程
t1 = threading.Thread(group=None,
target=sum,
name='t1')
t2 = threading.Thread(group=None,
target=sum,
name='t2')
t1.start()
t2.start()
3. 进程与线程的对比
3.1 关系对比
线程是依附在进程里面的,没有进程就没有线程。
一个进程默认提供一条线程,进程可以创建多个线程。
3.2 区别对比
进程之间不共享全局变量
线程之间共享全局变量,但是要注意资源竞争的问题,解决办法: 互斥锁或者线程同步
创建进程的资源开销要比创建线程的资源开销要大
进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
线程不能够独立执行,必须依存在进程中
多进程开发比单进程多线程开发稳定性要强
3.3 优缺点对比
进程优缺点:
优点:可以用多核
缺点:资源开销大
线程优缺点:
优点:资源开销小
缺点:不能使用多核