多进程

提问:什么时候使用多进程?
为什么不使用多线程?为什么不使用单进程

GIL(全球解释锁):机制就是在任意时刻,最后一个线程在运行
结果:一个python进程只会有一个线程在运行只会占用一个cpu的一个核
总结:因为python使用了GIL的机制导致一个python进程只会有一个线程在运行只会占用一个cpu的一个核

使用多进程:每个进程同一时刻最多运行一个线程
多进程运行的情况下,就会运行多个线程,会占用1个核。
选择依据方式:使用多核CPU进行计算只能选择多进程
多进程:做并发处理–》做压力测试程序+并发处理数据。

程序是同步的,不需要使用多核CPU多进程(同步是程序按顺序执行)
程序是异步的,就可以使用多核CPU多进程(异步是程序不按顺序执行)

协程:单线程运行,不使用锁,占用一个CPU
当使用协程的时候,运行多个任务,当某个任务有IO操作的时候
线程的cpu就进行切换到其他任务了
选择依据方式:IO操作多

多进程比较难–》只要不是必须用多进程的情况下你想简单的实现程序–》多线程----》使用协程
你想使用多核cpu资源计算的话只能使用多进程;如果不需要使用多核cpu,且io较多,用多线程或者协程都可以,两者推荐协程技术

只能说:多进程会利用多核cpu,怎么利用这个是操作系统控制的;cpu某一个核,真正在运行的时候只会运行一个线程

import os,time
from multiprocessing import Process

def worker():
print(“子进程执行中>>> pid={0},ppid={1}”.format(
os.getpid(),os.getppid()))
time.sleep(2)
print(“子进程终止>>> pid={0}”.format(os.getpid()))

def main():
print(“主进程执行中>>> pid={0}”.format(os.getpid()))

ps=[]
# 创建子进程实例
for i in range(2):
    p=Process(target=worker,name="worker"+str(i),args=()) #使用target表示这个进程要做什么,worker是一个函数;name是给进程起的名字;args=()表示worker函数是没有任何参数的,如果有参数,可以在args里指定
    ps.append(p)

# 开启进程
for i in range(2):
    ps[i].start()

# 阻塞进程
for i in range(2):
    ps[i].join()   #子进程执行完毕后,才能让主进程执行;如果不加这个函数,就会直接执行主程序

print("主进程终止")

if name == ‘main’:
main()

执行结果:
主进程执行中>>> pid=14180
子进程执行中>>> pid=12548,ppid=14180
子进程执行中>>> pid=6752,ppid=14180
子进程终止>>> pid=12548
子进程终止>>> pid=6752
主进程终止

进程的工作模式:
生成多个进程,在每个进程生成的时候指定每个进程执行的任务

另一种使用进程的方式:通过继承类进行重新定义
import os,time
from multiprocessing import Process

class MyProcess(Process):
def init(self):
Process.init(self)

def run(self): #没法给run方法传参数;规定不需要调用就可以使用
    print("子进程开始>>> pid={0},ppid={1}".format(os.getpid(),os.getppid()))
    time.sleep(2)
    print("子进程终止>>> pid={}".format(os.getpid()))

def main():
print(“主进程开始>>> pid={}”.format(os.getpid()))
myp=MyProcess()
myp.start()
myp.join()
print(“主进程终止”)

if name == ‘main’:
main()

执行结果:
主进程开始>>> pid=8780
子进程开始>>> pid=9192,ppid=8780
子进程终止>>> pid=9192
主进程终止

进程池:
在使用Python进行系统管理时,特别是同时操作多个文件目录或者远程控制多台主机,并行操作可以节约大量的时间。如果操作的对象数目不大时,还可以直接使用Process类动态的生成多个进程,十几个还好,但是如果上百个甚至更多,那手动去限制进程数量就显得特别的繁琐,此时进程池就派上用场了。
Pool类可以提供指定数量的进程供用户调用,当有新的请求提交到Pool中时,如果池还没有满,就会创建一个新的进程来执行请求。如果池满,请求就会告知先等待,直到池中有进程结束,才会创建新的进程来执行这些请求。

import os,time
from multiprocessing import Pool

def worker(arg):
print(“子进程开始执行>>> pid={},ppid={},编号{}”.format(os.getpid(),os.getppid(),arg))
time.sleep(0.5)
print(“子进程终止>>> pid={},ppid={},编号{}”.format(os.getpid(),os.getppid(),arg))

def main():
print(“主进程开始执行>>> pid={}”.format(os.getpid()))
ps=Pool(5) #个数尽量与CPU个数一致
for i in range(10):
#ps.apply(worker,args=(i,)) # 同步执行
ps.apply_async(worker,args=(i,)) # 异步执行

# 关闭进程池,停止接受其它进程,不加此句执行join会报错
ps.close()
# 阻塞进程
ps.join()
print("主进程终止")

if name == ‘main’:
main()

执行结果:
主进程开始执行>>> pid=9292 #进程号(PID)是由操作系统指定的,不是程序生成的
子进程开始执行>>> pid=6032,ppid=9292,编号0
子进程开始执行>>> pid=4188,ppid=9292,编号1
子进程开始执行>>> pid=9884,ppid=9292,编号3
子进程开始执行>>> pid=7692,ppid=9292,编号2
子进程开始执行>>> pid=4792,ppid=9292,编号4
子进程终止>>> pid=6032,ppid=9292,编号0
子进程终止>>> pid=4188,ppid=9292,编号1
子进程开始执行>>> pid=4188,ppid=9292,编号6
子进程开始执行>>> pid=6032,ppid=9292,编号5
子进程终止>>> pid=9884,ppid=9292,编号3
子进程终止>>> pid=7692,ppid=9292,编号2
子进程开始执行>>> pid=9884,ppid=9292,编号7
子进程开始执行>>> pid=7692,ppid=9292,编号8
子进程终止>>> pid=4792,ppid=9292,编号4
子进程开始执行>>> pid=4792,ppid=9292,编号9
子进程终止>>> pid=4188,ppid=9292,编号6
子进程终止>>> pid=6032,ppid=9292,编号5
子进程终止>>> pid=9884,ppid=9292,编号7
子进程终止>>> pid=7692,ppid=9292,编号8
子进程终止>>> pid=4792,ppid=9292,编号9
主进程终止

引出一个名字:线程安全和线程不安全

进程1 和进程2同时拿到a,100,别分别加了1,结束了,a=101----线程不安全
进程1拿到a=100,+1,a=101,进程2拿到a=101后,+1,a=102-----线程安全

引申一个问题:锁是干嘛的
进程1改a+1时候,加了一个锁,这个时候,其他进程都不能对a做任何操作
进程1操作完了,锁释放了,那么进程2才允许去做a+1的操作

import multiprocessing
import sys

#任务1:使用锁方法1
def worker_with(lock, f):
with lock:
fs = open(f,“a+”)
fs.write(‘Lock acquired via with\n’)
fs.close()

#任务2:使用锁方式2
def worker_no_with(lock, f):
lock.acquire()
try:
fs = open(f,“a+”)
fs.write(‘Lock acquired directly\n’)
fs.close()
finally:
lock.release()

if name == “main”:

f = "file.txt"

lock = multiprocessing.Lock()
w = multiprocessing.Process(target=worker_with, args=(lock, f))
nw = multiprocessing.Process(target=worker_no_with, args=(lock, f))

w.start()
nw.start()

w.join()
nw.join()

共享数据结构类型一:
from multiprocessing import Process

n=100 #在windows系统中应该把全局变量定义在if name == 'main’之上就可以了

def work():
global n
n=0
print('子进程内: ',n)

if name == ‘main’:
p=Process(target=work)
p.start()
p.join()
print('主进程内: ',n)

执行结果:
子进程内: 0
主进程内: 100

共享数据结构类型二:
使变量共享
#encoding=utf-8
import time
from multiprocessing import Process, Value, Lock

class Counter(object):
def init(self, initval = 0):
self.val = Value(‘i’, initval) #变量在所有的进程中共享
self.lock = Lock()

def increment(self):
    with self.lock:
        self.val.value += 1 # 共享变量自加1
        #print(“increment one time!”,self.value() ) 
         #加此句死锁

def value(self):
    with self.lock:
        return self.val.value

def func(counter):
for i in range(50):
time.sleep(0.01)
counter.increment()

if name == ‘main’:
counter = Counter(0) #实例化
procs = [Process(target = func, args = (counter,)) for i in range(10)] #使用推导列表循环10次
# 等价于
# for i in range(10):
# Process(target = func, args = (counter,))
for p in procs: p.start()
for p in procs: p.join()
print(counter.value())

执行结果:
500

共享数据结构类型三:(所有进程之间生成的数据能共享,方便统计)

#encoding=utf-8
from multiprocessing import Process, Manager

def f( shareDict, shareList ):
shareDict[1] = ‘1’
shareDict[‘2’] = 2
shareDict[0.25] = None
shareList.reverse() # 翻转列表

if name == ‘main’:
manager = Manager() #实例化
shareDict = manager.dict() # 创建共享的字典类型
shareList = manager.list( range( 10 ) ) # 创建共享的列表类型
p = Process( target = f, args = ( shareDict, shareList ) )
p.start()
p.join()
print(shareDict)
print(shareList)

执行结果:
{1: ‘1’, ‘2’: 2, 0.25: None}
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

进程间同步,使用队列(先进先出):保证先进的内容先被取出
#encoding=utf-8
from multiprocessing import Process, Queue

def offer(queue):

入队列

queue.put(“Hello World”)

if name == ‘main’:

创建一个队列实例

q = Queue()
p = Process(target = offer, args = (q,))
p.start()
print(q.get()) # 出队列
p.join()

执行结果:
Hello World

from multiprocessing import Process, Queue #队列的api

if name==‘main’:
q = Queue()
q.put(“hello”)
print(q.get())
for i in range(1):
q.put(i)
print(“q的长度”,q.qsize())
print(“q的长度”,q.full())
print(q.get(timeout=1) )#如果1秒内取不到数据
#print(q.get(timeout=1) )#如果1秒内取不到数据,就报超时间异常
#print(q.get_nowait() ) #没数据就抛异常

q1 = Queue(2)
q1.put(1)
q1.put(2)
print("q1的长度",q1.full())
q1.put_nowait(3) #写不进去抛queue.Full异常

作业:
使用队列方式,声明5个进程,并发的方式实现1-10.txt的文件中,每个文件写入10行英文,每写一行暂停0.1秒。使用单进程和多进程方式实现相同操作,看看时间差是多少。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值