

计算机原理


进程 | 线程 |
|---|---|
都是多任务编程方式 | |
都能使用计算机的多核资源 | |
空间独立,数据互不干扰,有专门的通信方法 | 使用全局变量通信 |
一个进程可以有多个分支线程,两者有包含关系 | |
多个线程共享进程资源,往往需要同步互斥处理 | |
不存在GIL问题 | 存在GIL问题 |
一个项目可能有多个进程 | 一个进程有多个线程 |
Python由于GIL问题往往使用多进程 | Java,C#之类的编程语言在执行多任务时一般都是用线程完成,因为线程资源消耗少 |
定义: 程序在计算机中的一次执行过程。

进程状态
就绪态 : 进程具备执行条件,等待系统调度分配 cpu 资源
运行态 : 进程占有 cpu 正在运行
等待态 : 进程阻塞等待,此时会让出 cpu

新建 : 创建一个进程,获取资源的过程
终止 : 进程结束,释放资源的过程

进程命令
ps -aux
进程树形结构
pstreeProcess()
功能 : 创建进程对象
参数 : target 绑定要执行的目标函数
args 元组,用于给target函数位置传参
kwargs 字典,给target函数键值传参
daemon bool值,让子进程随父进程退出p.start()
功能 : 启动进程注意 : 启动进程此时target绑定函数开始执行,该函数作为新进程执行内容,此时进程真正被创建
p.join([timeout])
功能:阻塞等待子进程退出
参数:最长等待时间进程创建示例:
"""
进程创建示例 01
"""
import multiprocessing as mp
from time import sleep
a = 1 # 全局变量
# 进程目标函数
def fun():
print("开始运行一个进程")
sleep(4) # 模拟事件执行事件
global a
print("a =",a) # Yes
a = 10000
print("进程执行结束")
# 实例化进程对象
process = mp.Process(target=fun)
# 启动进程 进程产生 执行fun
process.start()
print("我也做点事情")
sleep(3)
print("我也把事情做完了...")
process.join() # 阻塞等待子进程结束
print("a:",a) # 1 10000"""
进程创建示例02 : 含有参数的进程函数
"""
from multiprocessing import Process
from time import sleep
# 含有参数的进程函数
def worker(sec,name):
for i in range(3):
sleep(sec)
print("I'm %s"%name)
print("I'm working....")
# 元组位置传参
# p = Process(target=worker,args=(2,"Tom"))
# 关键字传参
p = Process(target=worker,
args = (2,),
kwargs={"name":"Tom"},
daemon=True) # 子进程伴随父进程结束
p.start()
sleep(3)
os.getpid()
功能: 获取一个进程的PID值
返回值: 返回当前进程的PID os.getppid()
功能: 获取父进程的PID号
返回值: 返回父进程PIDsys.exit(info)
功能:退出进程
参数:字符串 表示退出时打印内容"""
创建多个子进程
"""
from multiprocessing import Process
from time import sleep
import sys, os
def th1():
sleep(3)
print("吃饭")
print(os.getppid(), "--", os.getpid())
def th2():
# sys.exit("不能睡觉了") # 进程结束
sleep(1)
print("睡觉")
print(os.getppid(), "--", os.getpid())
def th3():
sleep(2)
print("打豆豆")
print(os.getppid(), "--", os.getpid())
# 循环创建子进程
jobs = [] # 存放每个进程对象
for th in [th1, th2, th3]:
p = Process(target=th)
jobs.append(p) # 存入jobs
p.start()
# 确保三件事都结束
for i in jobs:
i.join()
print("三件事完成")进程的基本创建方法将子进程执行的内容封装为函数。如果我们更热衷于面向对象的编程思想,也可以使用类来封装进程内容。
"""
自定义进程类
"""
from multiprocessing import Process
from time import sleep
class MyProcess(Process):
def __init__(self, value):
self.value = value
super().__init__() # 调用父类的init
# 重写run 作为进程的执行内容
def run(self):
for i in range(self.value):
sleep(2)
print("自定义进程类。。。。")
p = MyProcess(3)
p.start() # 将 run方法作为进程执行通信原理: 在内存中开辟空间,建立队列模型,进程通过队列将消息存入,或者从队列取出完成进程间通信。
实现方法
from multiprocessing import Queue
q = Queue(maxsize=0)
功能: 创建队列对象
参数:最多存放消息个数
返回值:队列对象
q.put(data)
功能:向队列存入消息
参数:data 要存入的内容
q.get()
功能:从队列取出消息
返回值: 返回获取到的内容
q.full() 判断队列是否为满
q.empty() 判断队列是否为空
q.qsize() 获取队列中消息个数
q.close() 关闭队列

注:类似于 multiprocessing 模块
from threading import Thread
t = Thread()
功能:创建线程对象
参数:target 绑定线程函数
args 元组 给线程函数位置传参
kwargs 字典 给线程函数键值传参
daemon bool值,主线程推出时该分支线程也推出启动线程
t.start()等待分支线程结束
t.join([timeout])
功能:阻塞等待分支线程退出
参数:最长等待时间__init__方法添加自己的属性,使用super()加载父类属性

from threading import Event
e = Event() 创建线程event对象
e.wait([timeout]) 阻塞等待e被set,且返回值为 e.is_set() 的值
e.set() 设置e,使wait结束阻塞
e.clear() 使e回到未被设置状态
e.is_set() 查看当前e是否被设置Event使用示例:
from threading import Thread, Event
msg = None # 通信变量
pairEvent = Event() # 事件对象
def give_message():
print("杨子荣前来拜山头")
pairEvent.wait() # 阻塞等待通知
global msg
msg = "宝塔镇河妖"
print(msg)
pairEvent.set()
Thread(target=give_message, daemon=True).start()
print("说对口令才是自己人")
print('天王盖地虎')
pairEvent.set() # 通知可以判断
pairEvent.clear() # 可能会出现死锁
pairEvent.wait()
if msg == "宝塔镇河妖":
print("确认过眼神你是对的人")
else:
print("打死他.... 无情啊 哥哥....")
from threading import Lock
lock = Lock() 创建锁对象
lock.acquire() 上锁 如果lock已经上锁再调用会阻塞,等待解锁。
lock.release() 解锁Lock使用示例:
from threading import Thread, Lock
lock = Lock() # 创建锁
a = b = 0
def value():
while True:
lock.acquire() # 上锁
if a != b:
print("a = %d,b = %d" % (a, b))
lock.release() # 解锁
t = Thread(target=value)
t.start()
while True:
lock.acquire()
a += 1
b += 1
lock.release()

死锁产生条件展开说明
为了避免死锁,我们需要有一个清晰的逻辑,防止同时出现上述死锁产生的四个条件,并且通过测试工程师进行的死锁检测。
由于 python 的 C 解释器设计中加入了解释器锁,导致python解释器同一时刻只能解释执行 一个线程,无法实现线程的并行。因此Python多线程并发在执行 多阻塞 任务时可以提升程序效率,其他情况并不能对效率有所提升。
注:线程遇到阻塞时线程会主动让出解释器,去解释其他线程。
关于GIL问题的处理