一、进程
(一)什么是进程
运行起来的应用程序称之为进程。
通俗的理解:不运行的时候是程序,运行起来就是进程。
程序和进程的对应关系是:程序只有一个,但是进程可以有多个
进程是系统进行资源分配和调度的基本单位
(二)创建多进程
from multiprocessing import Process
import time
def sing():
for i in range(10):
print('我在唱第{}句歌词'.format(i+1))
time.sleep(1)
def dance():
for i in range(10):
print('我在跳第{}段舞蹈'.format(i+1))
time.sleep(1)
if __name__ == '__main__':
t1 = Process(target=sing)
t2 = Process(target=dance)
t1.start()
t2.start()
主进程从main()开始执行,执行main函数体,当执行到t1.start()时,创建一个子进程,t1子进程中的代码和主进程相同,只是程序执行的开始是sing函数体。
主进程执行到t2.start()时,同样复制一份主进程代码从danc函数体开始执行
(三)进程的状态
程序有三个状态:就绪,运行和阻塞
(四)进程之间通讯
使用Queue队列,先进先出
两个进程通讯,就是一个子进程往queue中写内容,另一个进程从queue中取出数据
put()方法,向队列中存放数据。如果队列已满,此方法将阻塞直到有空间可用为止。
get(),返回q中的一个项目。如果q为空,此方法将阻塞,直到队列中有数据可用为止。
from multiprocessing import Queue
q = Queue(3) # 3表示容量
q.put('包子') #存放,如果队列已满,阻塞到有空间为止
q.put('香蕉')
q.put('西瓜')
print('执行到这了')
q.get() # 返回一个项目,如果队列已空,阻塞到有项目为止
q.put('猪肘子')
print('开始吃')
print(q.get()) # 先进先出
print(q.get())
print(q.get())
from multiprocessing import Queue
q = Queue(4)
q.put('包子')
q.put('香蕉')
q.put('西瓜')
while q.qsize() > 0: # qsize()队列中项目个数
print(q.get())
full()如果q已满,返回True
empty()如果调用此方法时q为空,返回True
from multiprocessing import Queue
q = Queue(3)
print(q.empty())
q.put(1)
q.put(2)
q.put(3)
print(q.full())
子进程与父进程
os.getpid():获取自己的进程ID
os.getppid():获取父进程ID
from multiprocessing import Process
import os
def sing(num):
print('参数是:', num, '进程的ID:', os.getpid(), '父进程:', os.getppid())
def dance(num):
print('参数是:', num, '进程的ID:', os.getpid(), '父进程:', os.getppid())
if __name__ == '__main__':
print('主进程ID:', os.getpid())
p1 = Process(target=sing, args=(9,))
p2 = Process(target=dance, args=(99,))
p1.start()
p2.start()
生产包子
生产与消费同时进行
from multiprocessing import Queue,Process
import time
def producer(q):
for i in range(10):
bz = '包子:%d'%(i+1)
print('生产'+ bz)
q.put(bz)
time.sleep(1)
def consumer(q):
for i in range(10):
bz= q.get()
print('消费'+bz)
if __name__ == '__main__':
q = Queue(3)
p1 = Process(target=producer, args=(q,))
p2 = Process(target=consumer, args=(q,))
p1.start()
p2.start()
进程子类化,重新编写run函数
from multiprocessing import Process
import os,time
class SubProcess(Process): #进程子类化
def __init__(self, x):
super().__init__()
self.x = x
def run(self):
for i in range(self.x):# 重新编写run函数,覆盖父类的run方法。进程启动的时候调用此方法
print('启动进程', i, os.getpid())
time.sleep(1)
if __name__ == '__main__':
p = SubProcess(3)
p.start()
p1 = SubProcess(3)
p1.start()
改编:添加生产名,以及确定被谁消费
from multiprocessing import Process, Queue
import time
class ProProcess(Process):
def __init__(self, q, name):
super().__init__()
self.q = q
self.name = name
def run(self):
for i in range(15):
bz = '包子:%d' % (i + 1)
print(self.name+'生产' + bz)
self.q.put(bz)
time.sleep(1)
class CusProcess(Process):
def __init__(self, q, name):
super().__init__()
self.q = q
self.name = name
def run(self):
for i in range(10):
bz = self.q.get()
print(self.name+'消费' + bz)
if __name__ == '__main__':
q = Queue(30) # 需要传入一个队列
p = ProProcess(q, 'pp ')
p1 = ProProcess(q, 'p11 ')
p.start()
p1.start()
p1.join() # join()主进程要等待该子进程执行完毕后,再执行其他子进程
print('#####################')
c = CusProcess(q, 'cc ')
c1 = CusProcess(q, 'c11 ')
c2 = CusProcess(q, 'c22 ')
c.start()
c1.start()
c2.start()
(五)进程池
进程池,达到进程重复利用
创建进程池对象的时候可以指定一个最大进程数,当有新的请求提交到进程池中,如果池中进程数还没有满,那么就会创建一个新的进程用来执行该请求,但是如果池中的进程数满了,该请求就会等待,知道进程池中的进程有结束的了,才会使用这个结束的进程来执行新的任务。
join 主进程等待所有子进程执行完毕,必须在close之后
close 等待所有进程结束才关闭进程池
from multiprocessing import Pool
import time
def hanshu1(name):
for i in range(5):
print(name, i)
time.sleep(1)
if __name__ == '__main__':
p = Pool(3)
a = 'abcde'
for x in a:
# p.apply(hanshu1, (x,)) # 阻塞,同时只跑一个进程
p.apply_async(hanshu1, (x,))# 非阻塞,同时进行几个子进程由Pool()里的参数决定
p.close()
p.join() # 阻塞主进程,等待子进程结束
from multiprocessing import Pool
import time
def zuoye(name):
print(name, '我在写代码')
time.sleep(1)
return name+'写完代码了'
def chouyan(status):
print('去抽烟是因为'+status)
if __name__ == '__main__':
p = Pool(1)
p.apply_async(zuoye, ('张三',), callback=chouyan)# callback回调
p.close()
p.join()
from multiprocessing import Pool
import time
def downLoad(movie):
for i in range(5):
print(movie, '下载进度%.2f%%' % ((i+1)/5*100))
time.sleep(1)
return movie
def alert(name):
print(name, '下载完毕,请收看')
if __name__ == '__main__':
movies = ['哪吒', '金刚葫芦娃', '黑猫警长', '小猪佩奇']
p = Pool(3)
for movie in movies:
p.apply_async(downLoad, args=(movie,), callback=alert)
p.close()
p.join()
二、线程
(一)线程概念
由于进程是资源拥有者,创建、撤销与切换存在较大的内存开销,因此需要引入轻型进程,即线程
进程是资源分配的最小单位,线程是CPU调度的最小单位(程序真正执行的时候调用的是线程),每一个进程中至少有一个线程
进程里的所有线程,共享进程的所有资源
(二)使用threading模块创建线程
from threading import Thread
import time
import os
def sing():
for i in range(10):
print('唱歌%d' % (i+1), os.getpid())
time.sleep(1)
def dance():
for i in range(10):
print('跳舞%d' % (i+1), os.getpid())
time.sleep(1)
if __name__ == '__main__':
t1 = Thread(target=sing)
t2 = Thread(target=dance)
t1.start()
t2.start()
setDaemon()方法
将当前线程设置成守护线程来守护主线程
传递参数
给函数传递参数,使用线程的关键字args=()进行传递参数
from threading import Thread
import time
def get(num):
for i in range(num):
print(i)
time.sleep(1)
if __name__ == '__main__':
t1 = Thread(target=get, args=(5,))
t1.setDaemon(True) # 只能写在start()之前
# 将当前线程设置成守护线程来守护主线程:
# 当主线程结束后,守护线程也就结束,不管是否执行完成
t1.start()
print('主线程结束了')
join()方法
当前线程执行完后其他线程才会继续执行
treading模块提供的方法
threading.currentThread():返回当前的线程变量
threading.enumerate():返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前。
threading.activeCount() 或 threading.active_count():返回正在运行的线程数量,与len(threading.enumerate())有相同效果
from threading import Thread
import threading
import time
def sing():
for i in range(10):
# threading.currentThread()返回当前的线程变量
print('唱歌%d'%(i+1),threading.current_thread())
time.sleep(1)
def dance():
for i in range(10):
print('跳舞%d'%(i+1),threading.current_thread())
time.sleep(1)
if __name__ == '__main__':
t1 = Thread(target=sing, name='刘德华')
t2 = Thread(target=dance, name='张惠妹')
t1.start()
t2.start()
# threading.enumerate()返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的进程
print(threading.enumerate())
# 返回正在运行的线程数量,与len(threading.enumerate())有相同效果
print(threading.activeCount())
print(threading.active_count())
t1.join()
t2.join()
print('结束') # 主线程结束,子线程继续执行
import threading,time
from threading import Thread
class T(Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
for i in range(10):
print(self.name, '跳舞%d'%(i+1))
time.sleep(1)
class C(Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
for i in range(10):
print(self.name, '唱歌%d'%(i+1))
time.sleep(1)
if __name__ == '__main__':
t = T('张三')
c = C('李四')
t.start()
c.start()
(三)共享全局变量的问题
多线程开发时共享全局变量会带来资源竞争效果。数据不安全。(出现脏数据)
import threading
g_num = 0
def hs1():
global g_num
for i in range(100000):
g_num+=1
print(g_num)
def hs2():
global g_num
for i in range(100000):
g_num += 1
print(g_num)
if __name__ == '__main__':
t1 = threading.Thread(target=hs1)
t2 = threading.Thread(target=hs2)
t1.start()
t2.start()