一、进程
进程 包含 线程 包含 协程
打开一个程序,产生一个进程,进程是程序的实体
一个任务就是一个进程
进程优点:稳定性高,一个进程崩溃了,不会影响其它进程
进程缺点:1、创建进程开销巨大
2、操作系统能同时运行进程数目有限
from multiprocessing import Process
process = Process(target=函数, name=进程的名字, args=(给函数传递的参数))
process 对象
对象调用方法:
process.start() 启动进程并执行任务
process.run() 只是执行了任务,但没有启动进程
process.terminate() 终止进程
多进程对于全局变量访问,在每一个全局变量里面都放一个m变量,
保证每个进程访问变量互不干扰。
m = 1 不可变类型
list1 = [] 可变类型
1、进程的创建
import os
from multiprocessing import Process
from time import sleep
m = 1 #不可变类型
list1 = [] #可变类型
def task1(s, name):
global m
while True:
sleep(s)
m += 1
#os.getpid() 进程号码 os.getppid() 父类进程号码
# print("这是任务1......", os.getpid(), '------', os.getppid(), name)
list1.append(str(m) + 'task1')
print("这是任务1......", m, list1)
def task2(s, name):
global m
while True:
sleep(s)
m += 1
# print('这是任务2...', os.getpid(), '------', os.getppid(), name)
list1.append(str(m) + 'task2')
print('这是任务2...', m, list1)
#主进程全局变量
number = 1
if __name__ == '__main__':
#主进程pid号码
print(os.getpid())
#子进程p
p = Process(target=task1, name='任务1', args=(1, 'aa'))
p.start()
print(p.name)
#子进程p1
p1 = Process(target=task2, name='任务2', args=(2, 'bb'))
p1.start()
print(p1.name)
while True:
number += 1
sleep(0.2)
if number == 100:
p.terminate()
p1.terminate()
break
else:
print(number)
2、进程的自定义
from multiprocessing import Process
class MyProcess(Process):
def __init__(self, name):
super(MyProcess, self).__init__()
self.name = name
#重写run方法
def run(self):
n = 1
while True:
# print('进程名字:' + self.name)
print('{}------>自定义进程, n:{}'.format(n, self.name))
n += 1
if __name__ == '__main__':
mp = MyProcess()
mp.start() #1、开新的进程 2、run()
mp1 = MyProcess()
mp1.start()
3、进程池 process-pool
当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程,但如果是上百甚至上千个目标,
手动的去创建进程的工作量巨大,此时就可以用到multiprocess模块提供的Pool方法,
初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满
那么就会创建一个新的进程用来执行该请求,但如果池中的进程数已经达到指定的最大值,
那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行。
非阻塞式: 全部添加到队列中,立刻返回,并没有等待其他的进程完毕,
但是回调函数是等待任务完成之后才调用。
进程池 设置进程数量 进程复用
(1)阻塞式 添加一个任务,执行任务,如果任务没有结束,任务无法增加
阻塞式特点:
添加一个任务,执行一个任务,如果一个任务不结束,另一个任务进不来
from multiprocessing import Pool
import time
from random import random
import os
def task(task_name):
print("开始做任务,", task_name)
start = time.time()
#使用sleep()
time.sleep(random() * 2)
end = time.time()
print('完成任务:{}用时:{},进程id:{}'.format(task_name, (end - start), os.getpid()))
if __name__ == '__main__':
pool = Pool(5)
tasks = ['听音乐', '吃饭', '洗衣服', '玩游戏', '散步', '睡觉', '做饭']
for task1 in tasks:
pool.apply(task, args=(task1,))
pool.close()
pool.join()
print('over......')
(2)非阻塞式
from multiprocessing import Pool
import time
from random import random
import os
def task(task_name):
print("开始做任务,", task_name)
start = time.time()
#使用sleep()
time.sleep(random() * 2)
end = time.time()
# print('完成任务:{}用时:{},进程id:{}'.format(task_name, (end - start), os.getpid()))
return '完成任务:{}用时:{},进程id:{}'.format(task_name, (end - start), os.getpid())
container = []
#回调
def callback_func(n):
container.append(n)
if __name__ == '__main__':
pool = Pool(5) #池只能装5个,Pool与主进程同执行,主进程完成,Pool也完成
tasks = ['听音乐', '吃饭', '洗衣服', '玩游戏', '散步', '睡觉', '做饭']
for task1 in tasks:
#池的非阻塞式
pool.apply_async(task, args=(task1,), callback=callback_func)
pool.close() #添加任务结束
pool.join() #阻挡主进程,并插入到主进程前面
for c in container:
print(c)
print('over...')
总结进程池:
pool = Pool(max) 创建进程池对象
pool.apply() 阻塞式
pool.apply_async() 非阻塞式
pool.close()
pool.join() 让主进程让步
4、进程间的通信 使用queue
进程1 通过queue,将数据传递到 进程2
stack 栈 push pop 先进后出
queue 队列 FIFO 先进先出
from multiprocessing import Queue
from multiprocessing import Process
from time import sleep
q = Queue(5)
q.put('A')
q.put('B')
q.put('C')
q.put('D')
q.put('E')
print(q.qsize()) #队列的长度
if q.full(): #判断队列是否已满 q.empty() 判断队列是否是空的
print('队列已满')
else:
q.put('F', timeout=3) # 队列满了,等待 put()如果队列满了,则只能等待,除非有'空地'则添加成功
#获取队列的值
print(q.get(timeout=2))
print(q.get(timeout=2))
print(q.get(timeout=2))
print(q.get(timeout=2))
print(q.get(timeout=2))
print(q.get(timeout=2))
print(q.get(timeout=2)) #报错 _queue.Empty 没有值
q.put_nowait()
q.get_nowait()
from multiprocessing import Queue
from multiprocessing import Process
from time import sleep
def download(q):
images = ['girl.jpg', 'boy.jpg', 'man.jpg']
for image in images:
print('正在下载:', image)
sleep(0.5)
q.put(image)
def getfile(q):
while True:
try:
file = q.get(timeout=5)
print('{}保存成功!'.format(file))
except:
print('全部保存完毕!')
break
if __name__ == '__main__':
q = Queue(5)
p1 = Process(target=download, args=(q,))
p2 = Process(target=getfile, args=(q,))
p1.start()
p1.join()
p2.start()
p2.join()
print('0000000')
二、线程
进程: Process
1、线程: Thread
线程:线程是进程中的一个实体,是程序执行流的最小单元 多线程 multithread 一个进程中的多个线程之间可以并发执行 一个程序:一个进程,启动程序 一个线程各干各的
进程是由操作系统找CPU分配空间(内存) 线程依赖于进程,可以执行例如耗时操作
考虑 创建线程? 如何使用线程? t1 = threading.Thread(target=download, name='aa', args=(1,)) 创建 t1.start() 运行
线程状态:新建 start 就绪 运行 结束 sleep()阻塞后,回到就绪 线程是可以共享全局变量的
from time import sleep
import threading
def download(n):
images = ['girl.jpg', 'boy.jpg', 'man.jpg']
for image in images:
print('正在下载:', image)
sleep(0.6)
print('下载{}成功!'.format(image))
def listenMusic():
musics = ['aaa', 'bbb', 'ccc', 'ddd']
for music in musics:
print('正在听{}歌曲!'.format(music))
if __name__ == '__main__':
#线程对象
t1 = threading.Thread(target=download, name='aa', args=(1,))
t1.start()
t2 = threading.Thread(target=listenMusic, name='bb')
t2.start()
n = 1
while True:
print(n)
sleep(3)
n += 1
2、线程是共享全局变量的
GIL global iterable lock 全局解释器锁
数据不安全 线程同步速度慢
添加lock后,数据安全
python底层只要使用线程,默认加锁 进程:计算密集型(计算量大),运算快速 线程:耗时操作,网络下载,爬虫,IO
import threading
ticket = 1000
def pay_ticket():
global ticket
for i in range(100):
ticket -= 1
if __name__ == '__main__':
t1 = threading.Thread(target=pay_ticket, name='th1')
t2 = threading.Thread(target=pay_ticket, name='th2')
t3 = threading.Thread(target=pay_ticket, name='th3')
t4 = threading.Thread(target=pay_ticket, name='th4')
t1.start()
t2.start()
t3.start()
t4.start()
t1.join()
t2.join()
t3.join()
t4.join()
print('ticket_number', ticket)
3、共享数据
如果多个线程共同对某个数据修改,则可能出现不可预料的结束,为了保证数据的正确性,需要对多个线程进行同步。
同步:一个一个的完成,一个做完,另一个才能进来
效率就会降低。
使用Thread对象的Lock和Rlock可以实现简单的线程同步, 这两个对象都有acquire方法和release方法,对于哪些需要每次只 允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。
多线程的优势在于可以同时运行多个任务(至少感觉起来是这样),但是当线程需要共享数据时, 可能存在数据不同步的问题,为了避免这种情况,引入了锁的概念。
lock = threading.Lock()
lock.acquire() 请求得到锁
......
lock.release() 释放锁
只要不释放,其它的线程都无法进入运行状态
import threading
import random
import time
lock = threading.Lock()
list1 = [0] * 10
#列表赋值
def task1():
#获取线程锁,如果已经上锁,则阻塞并等待锁的释放
lock.acquire() #阻塞
for i in range(len(list1)):
list1[i] = 1
time.sleep(0.5)
lock.release() #释放
#打印列表值
def task2():
lock.acquire()
for i in range(len(list1)):
print('--->', list1[i])
time.sleep(0.5)
lock.release()
if __name__ == '__main__':
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t2.start()
t1.start()
t2.join()
t1.join()
print('over', list1)
4、死锁
开发过程中使用线程,在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。 尽管死锁很少发生,但一旦发生就会造成应用的停止响应,程序不做任何事情。
资源分配先后顺序不当,会造成死锁,应当避免死锁
解决死锁: 1、重构代码 2、使用timeout参数
from threading import Thread, Lock
import time
lockA = Lock()
lockB = Lock()
class MyThread(Thread):
def run(self): #start
if lockA.acquire(): #如果可以获取到锁,则返回True
print(self.name + '获取了A锁')
time.sleep(0.1)
if lockB.acquire(timeout=5):
print(self.name + '又获取了B锁')
lockB.release()
lockA.release()
class MyThread1(Thread):
def run(self): #start
if lockB.acquire(): #如果可以获取到锁,则返回True
print(self.name + '获取了A锁')
time.sleep(0.1)
if lockA.acquire(timeout=5):
print(self.name + '又获取了B锁')
lockA.release()
lockB.release()
if __name__ == '__main__':
t1 = MyThread()
t2 = MyThread1()
t1.start()
t2.start()
5、生产者与消费者 两个线程之间的通信
一个线程为生产者,另一个线程为消费者
Python的queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出) 队列Queue,LIFO(先入后出)
队列 LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语(可以理解为原子操作,即要么不做,要么就做完),能够在线程中直接使用。
可以使用队列来实现线程间的同步
import threading
import queue
import random
import time
#生产者
def produce(q):
i = 0
while i < 10:
num = random.randint(1, 100)
q.put("生产者产生的数据:%d" % num)
print("生产者产生的数据:%d" % num)
time.sleep(1)
i += 1
q.put(None)
#完成任务
q.task_done()
#消费者
def consume(q):
while True:
item = q.get()
if item is None:
break
print("消费者获取到:%s" % item)
time.sleep(4)
#完成任务
q.task_done()
if __name__ == '__main__':
q = queue.Queue(10)
arr = []
#创建生产者
th = threading.Thread(target=produce, args=(q,))
th.start()
#创建消费者
tc = threading.Thread(target=consume, args=(q,))
tc.start()
th.join()
tc.join()
print('END')
6、总结线程 Thread
1、创建线程
内置线程:
t = Thread(target=func, name='', args=(), kwargs={})--->新建状态
t.start()--->就绪状态
run()
join() 主线程让步
自定义线程:
class MyThread(Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
任务
t = MyThread()
t.start()
2、数据共享
进程的共享的线程共享数据区别:
进程是每个进程都有一份
线程是共用一个数据--->数据安全性问题
GIL--->伪线程
lock = Lock()
lock.acquire()
...
lock.release()
只要用锁:顺序有问题 死锁
避免死锁
3、线程间通信:生产者与消费者
生产者:线程
消费者:线程
import queue
q = queue.Queue()
创建生产者
th = threading.Thread(target=func, name='', args=(), kwargs={})
th.start()
tc = threading.Thread(target=func, name='', args=(), kwargs={})
tc.start()
q.put() 放值
q.get() 取值
查看GIL文章
三、协程
协程:微线程
进程 线程 协程
在线程中开启多个任务
Process Thread 底层为生成器完成
协程:耗时操作 高效利用CPU
耗时操作:网络请求(网络下载(爬虫)、上传)、
IO操作(文件的读写)、sleep阻塞
高效利用CPU,高效利用资源
不同执行操作的切换
生成器;yield
import time
def task1():
for i in range(3):
print('A' + str(i))
yield
time.sleep(0.2)
def task2():
for i in range(3):
print('B' + str(i))
yield
time.sleep(0.2)
#两个任务交替运行
if __name__ == '__main__':
g1 = task1()
g2 = task2()
while True:
try:
next(g1)
next(g2)
except:
break
使用greenlet,可以完成协程任务,并不用写yield
pip install greenlet
import time
from greenlet import greenlet
def a(): #任务A
for i in range(5):
print('A' + str(i))
gb.switch()
time.sleep(0.1)
def b(): # 任务B
for i in range(5):
print('B' + str(i))
gc.switch()
time.sleep(0.1)
def c(): # 任务C
for i in range(5):
print('C' + str(i))
ga.switch()
time.sleep(0.1)
if __name__ == '__main__':
ga = greenlet(a)
gb = greenlet(b)
gc = greenlet(c)
ga.switch()
四、总结进程、线程
进程,线程
Process类
创建进程
def func(n):
pass
p = Process(target=func, name='', args=(1,), kwargs='')
p.start()
run
class MyProcess(Process):
def run(self):
pass
p = MyProcess()
p.start()
进程数据共享
进程池:Pool 可以创建多个进程
p = Pool(5)
阻塞式 apply(func, args, kwargs) 一个一个执行
非阻塞式 apply_async(func, args, kwargs, callback='') 根据最大容量将进程一起添加到里面执行
callback 进程完成后调用的函数,例如下载图片后在callback函数中渲染到手机上
进程间通信:
Queue()
q = Queue(5)
q.put()
q.get()
q.qsize()
q.empty()
q.full()
线程:
进程里面可以存着多个线程,多个线程共用进程资源
t = Thread(target=func, name='', args=(1,), kwargs='')
t.start()
GIL:全局解释器锁
Python自带,线程:伪线程 加锁不能提高效率
快速计算,用进程
耗时操作:用线程