进程锁
进程锁
一:简介
多进程数据不共享,java中只有多线程没有多进程可以充分发挥cpu的性能,但python中多线程不能发挥cpu的性能,同一时间点只有一个线程可以被cpu执行,即使的多核的,也只有一个线程被执行,因为全局解释器锁GIL的存在,直到现在也没有解决,尽管这个问题存在,但是并不会对python的效率有太大的影响.
计算密集型:写的程序全是计算的逻辑,没有和硬盘打交道或者远程数据库的IO操作,这时候使用多进程来解决并发问题,实现高效的计算,几乎没有这种场景,因为都要和数据库交互.
IO密集型:现在的大多数写的程序都是和数据库交互,或者redis热数据打交道,IO很多,计算很少,这样就使用多线程来解决.
所以GIL并不会影响Python的继续流行.
java中的并发包concurrent中里面提供了许多的锁,信号量,并发容器更是多种多样,但是感觉很笨重,光是不同线程池里面的队列就封装了四种,而Python就简单的多.
计算密集型不会在一台计算机上计算,而是换分布式计算架构,就会用大数据的map-reduce思想,map到各个节点来计算,然后reduce汇总数据.
多进程一般用的少,除非写的是计算密集型,当遇到计算密集型就切换到Go语言中.这里留个坑,链接.
二:Lock
早期的多进程就是在一台计算机上,用的socket为底层原理,文件为形式的数据共享,现在多进程都是使用消息中间件来数据共享
但是多线程的形式和多进程几乎无异,所以有必要提前了解下.
1 from multiprocessing import Process,Lock 2 import os 3 import time 4 def work(mutex): 5 mutex.acquire() 6 print('task[%s] 上厕所'%os.getpid()) 7 time.sleep(3) 8 print('task[%s] 上完厕所'%os.getpid()) 9 mutex.release() 10 if __name__ == '__main__': 11 mutex = Lock()
#先创建锁,然后作为数据传入各个进程当中,请求锁就acquire,释放锁就release 12 p1 = Process(target=work,args=(mutex,)) 13 p2 = Process(target=work,args=(mutex,)) 14 p3 = Process(target=work,args=(mutex,)) 15 p1.start() 16 p2.start() 17 p3.start() 18 p1.join() 19 p2.join() 20 p3.join() 21 print('主')
进程级别操作内存数据是绝对安全的,但是操作文件是会发生数据不安全的.
with lock: a+=1 print("Abc")
将需要加锁的写在with lock:上下文里面,防止异常出现或者忘了release()释放锁而导致的长时间阻塞.
三:进程之间通信
IPC:inter process communication
主进程创建Queue,传到子进程中即可,基于文件的socket实现的,里面有一把锁,拿和取都加了锁,天生数据安全的
几乎用不到,只是了解
Pipe是数据不安全的,同样基于文件的socket和pickle,当涉及到无需更改数据,且只有一个进程的时候,就用pipe,没有竞争,没有修改数据,只是一个拿一个取.
Queue里面的empty() full() qsize()全部都是不准的,因为没有锁,一瞬间的并不准确
而get和put方法是进程安全的
四:生产者消费者模型
Queue在满了的时候放不进内存,所以他可以防止一次性放入内存太多数据,内存过多,而模式本身又平衡了二者
问题是队列空了的时候消费者会一直阻塞在get()哪里等待消费,而empty()方法又是不可靠的
生产者消费者爬虫

import requests import re from multiprocessing import Process,Queue def producer(q,url): resp = requests.get(url) q.put(resp.text) def consumer(q,compiled_pattern): while True: s = q.get() if not s:break result = compiled_pattern.finditer(s) for i in result: print( { "id":i.group("id"), "title":i.group("title"), "score":i.group("score"), "comment_num":i.group("comment_num") }) if __name__ == "__main__": pattern = '<div class="item">.*?<em.*?>(?P<id>\d+).*?<span.*?>(?P<title>.*?)</span>.*?<span class="rating_num".*?>(?P<score>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>' compiled_pattern = re.compile(pattern, re.S) q = Queue(3) num = 0 p_list = [] for i in range(10): url = "https://movie.douban.com/top250?start=%s&filter="%num num += 25 p = Process(target = producer,args = (q,url)) p.start() p_list.append(p) c = Process(target = consumer,args = (q,compiled_pattern)).start() for i in p_list: i.join() q.put(None)
p = Process(target = producer,args = (q,url)).start()
p_list.append(p)
当时写的时候报错,NoneType has no arrtibute "join"就是这里出的错,因为start()没有返回值,放不进去列表,要分开写.
五:joinablequeue
解决刚才消费者一直while True:拿数据,因为While True很费资源和性能
这个队列多了一个功能就是消费者消费完了有一个通知功能,q.task_done(),几乎用不到.
六:进程间数据共享
进程之间数据不能共享,但是就是提供了这么个机制,知道有人没事干搞了一个这么个玩意就行,几乎也是用不到.
Manager类
m=Manager() #这个字典是共享的了 new_dict = m.dict({"A":1})
然后开进程传入到每个进程里面,无论哪个进程修改了数据,其他进程都可以感知到,但是不是并发容器,需要加锁保证数据安全
支持with上下文管理.
忘掉这个,这辈子都别用,又慢又不安全!!