程序与进程
程序
- Program,一系列指令的集合
- 使用编程语言所编写的指令的集合,用于实现一定的功能
进程
- 启动后的程序在系统中就有一个进程,系统会为这个进程分配内存空间
创建进程的方式
os.fork()函数
- 只适用于Unix、Linux、Mac操作系统
multiprocessing模块
- 自定义类继承Process
- 重写run方法
- 语法结构:
Process(group=None, target, name, args, kwargs)
from multiprocessing import Process
import os, time
# 定义函数,函数中的代码就是进程要执行的代码
def test():
print(f"我是子进程,我的PID是:{os.getpid()},我的父进程是:{os.getppid()}")
if __name__ == '__main__':
start = time.time()
lst = []
print('主进程开始执行')
# 创建5个子进程
for i in range(5):
# 创建一个子进程对象
pro = Process(target=test)
# 启动进程
pro.start()
# 启动的进程添加到列表中
lst.append(pro)
# 列表中的每个成员都是一个进程
for item in lst:
# 阻塞主进程
item.join()
print(f'运行5个子进程一共花了:{time.time() - start}')
print('主进程执行结束') # 只是主进程钟没有代码了,并不是主进程结束,等待子进程执行结束主进程才结束
Pool进程池
- 语法结构:
p = Pool(N)
from multiprocessing import Pool
import time
import os
# 编写任务
def task(name):
print(f'子进程的PID:{os.getpid()},执行任务 --- task --- {name}')
time.sleep(1)
if __name__ == '__main__':
start = time.time()
print('父进程开始执行')
# 创建一个进程池
p = Pool(3) # 进程池里,最大进程的个数3
# 创建10个进程
for i in range(10):
# 以非阻塞方式进行
# p.apply_async(func=task, args=(i,))
# 以阻塞方式进行
p.apply(task, args=(i,))
p.close() # 关闭进程池,不在接收新任务
p.join() # 阻塞父进程,等待子进程结束
print('所有子进程结束,父进程结束')
print(time.time() - start)
Process类的常用方法和属性
from multiprocessing import Process
import os
import time
def sub_precess1(name):
print(f'子进程PID: {os.getpid()},父进程PID:{os.getppid()} -------- {name}')
time.sleep(1)
def sub_precess2(age):
print(f'子进程PID: {os.getpid()},父进程PID:{os.getppid()} -------- {age}')
time.sleep(1)
if __name__ == '__main__':
print('父进程开始执行')
for i in range(5):
# 创建子进程1
p1 = Process(target=sub_precess1, args=('ivan',))
# 创建子进程2
p2 = Process(target=sub_precess2, args=('18',))
# 启动子进程
p1.start()
p2.start()
print(p1.name, '是否执行完毕', p1.is_alive())
print(p2.name, '是否执行完毕', p2.is_alive())
print(p1.name, '的PID是:', p1.pid)
print(p2.name, '的PID是:', p2.pid)
p1.join() # 等待p1执行结束
p2.join() # 等待p2执行结束
print('父进程执行结束')
from multiprocessing import Process
import os
import time
def sub_precess1(name):
print(f'子进程PID: {os.getpid()},父进程PID:{os.getppid()} -------- {name}')
time.sleep(1)
def sub_precess2(age):
print(f'子进程PID: {os.getpid()},父进程PID:{os.getppid()} -------- {age}')
time.sleep(1)
if __name__ == '__main__':
print('父进程开始执行')
for i in range(5):
# 创建子进程1
# p1 = Process() # 没有给定target参数,默认调用run方法
# 创建子进程2
# p2 = Process()
# 启动子进程
# p1.start() # 自动调用run方法执行
# p2.start()
# p1.run()
# p2.run()
p1 = Process(target=sub_precess1, args=('ivan',))
p2 = Process(target=sub_precess2, args=(18,))
p1.start()
p2.start()
# 终止进程
p1.terminate()
p2.terminate()
print('父进程执行结束')
多个进程同时执行的顺序是随机的,不会根据创建的顺序执行
并发和并行
并发
- 两个或多个事件同一时间间隔发生,多个进程被交替轮换着执行
并行
- 两个或多个事件在同一时刻发生,多条命令在同一时刻在多个处理器上同时执行
进程之间的通信
多个进程间的数据可以共享吗?
- 全局变量a在父进程和两个子进程中各一份,各自操作各自a的值,对于a的计算结果并没有在进程之间传递,进程之间并没有共享数据
from multiprocessing import Process
a = 100
def add():
print('子进程1开始执行')
global a
a += 30
print('a=', a)
print('子进程1执行结束')
def sub():
print('子进程2开始执行')
global a
a -= 50
print('a=', a)
print('子进程2执行结束')
if __name__ == '__main__':
print('父进程开始执行')
print('a的值为:', a)
# 创建加的进程
p1 = Process(target=add)
# 创建减的进程
p2 = Process(target=sub)
# 启动两个子进程
p1.start()
p2.start()
# 等待进程执行结束
p1.join()
p2.join()
print('父进程执行结束')
进程之间的通信
队列(Queue)
- 先进先出,First In First Out
multiprocessing模块的Queue类
语法结构q = Queue(N)
from multiprocessing import Queue
if __name__ == '__main__':
# 创建一个队列,最多可以接收3条消息
q = Queue(3)
print('队列是否为空:', q.empty())
print('队列是否为满:', q.full())
# 向队列中添加消息
q.put('hello') # block默认为True
q.put('world')
print('队列是否为空:', q.empty())
print('队列是否为满:', q.full())
q.put('python')
print('队列是否为空:', q.empty())
print('队列是否为满:', q.full())
print('队列当中消息的个数:', q.qsize())
# 队列中已经满了,还能否添加数据呢?
# q.put('html') # 队列已满,block=True默认为True,一直等待队列有空位置才会将html入队,然后继续执行
# q.put('html', block=True, timeout=2) # 等待两秒,还灭有空位置,抛出异常queue.Full
# q.put_nowait('html') # 相当于q.put('html', block=False) # 抛出异常queue.Full
print('---------------')
# 从列表中获取消息,出队操作
print(q.get())
print('出队之后,消息的个数:', q.qsize())
q.put_nowait('html') # 入队
print('入队之后,消息的个数:', q.qsize())
# 通过遍历出队所有元素
if not q.empty(): # 判断队列是否为空
for i in range(q.qsize()):
print(q.get_nowait())
print('队列是否为空:', q.empty())
print('队列是否为满:', q.full())
print('队列当中消息的个数:', q.qsize())
使用队列实现进程之间的通信
- 一个进程负责向队列中写入数据
- 一个进程负责从队列中读取数据
from multiprocessing import Process, Queue
import time
a = 100
# 向队列中写入消息的进程要执行的函数
def write_msg(q):
global a
if not q.full():
for i in range(1, 6):
a -= 10
q.put(a) # 入队操作,将a的值进行入队
print('a入队时的值:', a)
# 从队列中读取消息的进程要执行的函数
def read_msg(q):
time.sleep(1)
while not q.empty():
print('出队a的值:', q.get())
if __name__ == '__main__':
print('父进程开始执行')
q = Queue() # 有父进程创建队列,传给子进程,没有写个数,说明队列可接收的消息个数没有上限
# 创建两个子进程
p1 = Process(target=write_msg, args=(q,))
p2 = Process(target=read_msg, args=(q,))
# 启动两个子进程
p1.start()
p2.start()
# 等待写入进程结束
p1.join()
# 等待读取进程结束
p2.join()
print('-----父进程执行结束-----')
多任务的方式
- 一个应用程序内使用多个进程
- 一个进程内使用多个线程
线程
- CPU 可调度的最小单位
- 被包含在进程中,是进程中实际的运作单位
- 一个进程中可以并发多个线程,每条线程并行执行不同的任务
创建线程的方式
- 使用threading模块创建线程
- 使用函数式创建线程:
t = Thread(group, target, name, args, kwargs)
# 多线程
import threading
from threading import Thread
import time
# 编写函数
def test():
for i in range(3):
time.sleep(1)
print(f'线程{threading.current_thread().name}正在执行', {i})
if __name__ == '__main__':
start = time.time()
print('主线程开始执行')
# for i in range(2):
# t = Thread(target=test)
# # 启动线程
# t.start()
# t.join()
lst = [Thread(target=test) for i in range(2)] # 使用列表生成式
for item in lst:
item.start()
for item in lst:
item.join()
print('一共耗时:', time.time() - start)
使用Tread子类创建线程
- 自定义类继承Thread类
- 实现run方法
# 继承方式创建线程
import threading
from threading import Thread
import time
class SubThread(Thread):
def run(self):
for i in range(3):
time.sleep(1)
print(f'Thread {threading.current_thread().name} started, i = {i}')
if __name__ == '__main__':
print('主线程开始执行')
lst = [SubThread() for i in range(2)] # 创建两个线程,放到列表当中存储
for item in lst:
item.start()
for item in lst:
item.join()
print('主线程执行结束')
线程之间的数据共享
线程之间的通信
- 一个进程内的所有线程共享全局变量
from threading import Thread
a = 100
def add():
print('加的线程开始执行')
global a
a += 30
print(f'a的值为:{a}')
print('加的线程执行结束')
def sub():
print('减的线程开始执行')
global a
a -= 50
print(f'a的值为:{a}')
print('减的线程执行结束')
if __name__ == '__main__':
print('主线程开始执行')
print(f'--------全局变量a的值{a}--------')
add = Thread(target=add)
sub = Thread(target=sub)
# 启动线程
add.start()
sub.start()
add.join() # 等待加的线程结束
sub.join() # 等待减的线程结束
print('--------主线程执行结束--------')
线程操作共享数据的安全性问题
多线程共享数据所带来的安全性问题
- 多线程都是在同一个进程中运行的,因此在进程中的全局变量所有线程都可以共享,这就造成了一个问题,因为线程的顺序是无序的,有可能会造成数据错乱
import threading
from threading import Thread
import time
ticket = 50
def sale_ticket():
global ticket
for i in range(1000):
if ticket > 0:
print(threading.current_thread().name + f'正在出售第{ticket}张票')
ticket -= 1
time.sleep(1)
if __name__ == '__main__':
lst = [Thread(target=sale_ticket) for i in range(3)]
for item in lst:
item.start()
锁机制
- 互斥锁的两种状态:锁定和非锁定
- 当线程访问共享资源时,先将其“锁定”,其他线程不能修改;直到该线程释放资源,将资源的状态变成“非锁定”时,其他线程才能操作共享资源
锁机制
threading中的Lock类
创建锁对象:lock_obj = Lock()
Lock类的方法
- 上锁(锁定):obj.acquire()
- 释放锁:obj.release()
import threading
from threading import Thread, Lock
import time
ticket = 50
lock_obj = Lock() # 创建锁对象
def sale_ticket():
global ticket
for i in range(1000):
lock_obj.acquire() # 加锁
if ticket > 0:
print(threading.current_thread().name + f'正在出售第{ticket}张票')
ticket -= 1
lock_obj.release() # 释放锁
time.sleep(1)
if __name__ == '__main__':
lst = [Thread(target=sale_ticket) for i in range(3)]
for item in lst:
item.start()
使用锁的原则
- 把尽量少的和不耗时的代码放到锁中执行
- 代码执行完成后要记得释放锁
生产者模式和消费者模式
多个线程之间通信可以采用生产者模式和消费者模式
生产者线程
- 生产者线程用于“生产”数据
消费者线程
- 消费者线程用于“处理”数据
线程中的队列
Queue可以实现进程之间的通信,也可以实现线程之间的通信
使用Queue实现生产者与消费者问题
- 生产者将“数据”放入队列
- 消费者从队列中“取出”数据
from queue import Queue # 实现线程之间的通信
# from multiprocessing import Queue
from threading import Thread
import time
# 创建一个生产者类
class Producer(Thread):
def __init__(self, name, queue):
Thread.__init__(self, name=name)
self.queue = queue
def run(self):
for i in range(1, 6):
print(f'{self.name} 将产品 {i} 放入队列')
self.queue.put(i)
time.sleep(1)
print('生产者完成了所有数据的存放')
# 创建一个消费者类
class Consumer(Thread):
def __init__(self, name, queue):
Thread.__init__(self, name=name)
self.queue = queue
def run(self):
for _ in range(5):
value = self.queue.get()
print(f'消费者线程 {self.name} 取出了 {value}')
time.sleep(1)
print('-------- 消费者线程完成了所有数据的取出 --------')
if __name__ == '__main__':
# 创建队列
queue = Queue()
# 创建生产者线程
producer = Producer('producer', queue)
# 创建消费者线程
consumer = Consumer('consumer', queue)
# 启动线程
producer.start()
consumer.start()
# 等待生产者线程结束,等待消费者线程结束
producer.join()
consumer.join()
print('主线程运行结束')