Loading

进程锁

进程锁

一:简介

多进程数据不共享,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)
View Code

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上下文管理.

忘掉这个,这辈子都别用,又慢又不安全!!

posted @ 2019-10-07 11:09  浅忆尘  阅读(491)  评论(0)    收藏  举报