多线程、多进程和线程池编程

GIL (global interpreter lock)全局解释器锁

基于cpython的介绍:
python中的一个线程是对应于c语言中的一个线程,python在早期的时候为了简单,会在解释器上加一把非常大的锁,允许我们同一时刻只有一个线程运行在一个cpu上执行字节码。在某种程度上保证了我们的线程是安全的。它无法将多个线程映射到多个cpu上执行,使得我们无法利用多核优势。
gil使得同一时刻只有一个线程在一个cpu上执行字节码

python现在正在努力的去掉gil,像pypy是去gil的.因为cpython当中的许多第三方包使用gil的,所以短期类gil是不太可能被去掉的。

那么是不是说在一个时刻只有一个线程运行在我们的cpu上,就意味着我们在使用多线程编码的时候就是安全的呢?就不用去考虑线程之间的同步呢?实际上不是的。因为gil在某些情况下是会被释放的。gil的释放在python2和python3还有区别的。

gil的释放:
1.gil会根据执行的字节码行数以及时间片释放gil
2.gil还会在遇到io操作的时候主动释放

所以python在io操作的时候,多线程编程就变得非常重要了。



# import dis
# def  add(a):
#     a=a+1
#     return a
# #我们可以对它进行反编译的
# print(dis.dis(add))
'''
H:\ANACONDA\python.exe I:/ainlp/pythonHight/chapter11/pythonGIL.py
  3           0 LOAD_FAST                0 (a)
              2 LOAD_CONST               1 (1)
              4 BINARY_ADD
              6 STORE_FAST               0 (a)

  4           8 LOAD_FAST                0 (a)
             10 RETURN_VALUE
None
'''
total=0
def add():
    global total
    for i in range(1000000):
        total+=1
def  desc():
    global  total
    for i in range(1000000):
        total-=1
import threading
thread1=threading.Thread(target=add)
thread2=threading.Thread(target=desc)
thread1.start()
thread2.start()


thread1.join()
thread2.join()
print(total)



多线程编程

操作系统能够调度的最小单元是线程,在最开始的时候操作系统能够调度的最小单元实际上是进程,但是因为进程对于系统的资源消耗非常大,所以后期就演变出了线程。线程实际上是依赖于进程的。如在windows任务管理器中会看到许多的进程,如下图:
在这里插入图片描述

对于操作系统来说能够调度的做小单元是线程。

多线程编程:
1.通过Thread类实例化

# 对于io操作来说,多线程和多进程差别不大
import time
import threading
#1.通过Thread类实例化

def get_detail_html(url):
    print("get detail html started")
    time.sleep(2)
    print("get detail html end")
def get_detail_url(url):
    print("get detail url started")
    time.sleep(4)
    print("get detail url end")
if __name__=="__main__":
    thread1=threading.Thread(target=get_detail_html,args={"",})
    thread2 = threading.Thread(target=get_detail_url, args={"",})
    #设置子进程为守护线程  当主线程退出的时候,子线程也会被关掉。
    # thread1.setDaemon(True)
    thread2.setDaemon(True)

    start_time=time.time()
    thread1.start()
    thread2.start()
    #那么如何使得在两个线程运行完成之后再执行主线程呢
    thread1.join()
    thread2.join()
    '''
    H:\ANACONDA\python.exe I:/ainlp/pythonHight/chapter11/python_thread.py
    get detail html started
    get detail url started
    get detail html end
    get detail url end
    last time: 4.0012288093566895
    在加入线程阻塞之后,主线程会等待两个子线程执行完成之后才开始执行,而两个子线程
    运行所用的时间并不是顺序运行的时间,而是在一定程度的并行运行,因此是花费了线程
    2所用的时间。
    '''


    '''
    虽然在这个python脚本当中创建了2个线程,但是实际上一共有3个线程
    第三个线程就是我们的python程序,即所说的主线程。
    
    '''
    print("last time: {}".format(time.time()-start_time))

2.通过继承Thread来实现多线程 适用于复杂的场景

import time
import threading
class GetDetailHtml(threading.Thread):
    #重载init方法
    def __init__(self,name):
        super().__init__(name=name)

    #重载run方法
    def  run(self):
        print("get detail html started")
        time.sleep(2)
        print("get detail html end")
class GetDetailUrl(threading.Thread):
    # 重载init方法
    def __init__(self,name):
        super().__init__(name=name)

    # 重载run方法
    def run(self):
        print("get detail url started")
        time.sleep(4)
        print("get detail url end")
#通过这两个类的实例完成我们的多线程
if __name__=="__main__":
    thread1=GetDetailHtml("getdetailhtml")
    thread2= GetDetailUrl("getdetailurl")
    # thread1.setDaemon(True)
    thread2.setDaemon(True)

    start_time=time.time()
    thread1.start()
    thread2.start()
    #那么如何使得在两个线程运行完成之后再执行主线程呢
    thread1.join()
    thread2.join()
    print("last time: {}".format(time.time() - start_time))

线程间通信

1.线程间共享变量

import time
import threading
from pythonHight.chapter11.variables import detail_url_list
from pythonHight.chapter11 import variables


#1.通过Thread类实例化


#爬取文章详情页
def get_detail_html():
    detail_url_list=variables.detail_url_list
    # while True:
    if len(detail_url_list):
        # global  detail_url_list
        url=detail_url_list.pop()
        # for url in detail_url_list:
        print("get detail html started")
        time.sleep(0.5)
        print("get detail html end")

#爬取文章列表页
def get_detail_url():
    # global  detail_url_list
    detail_url_list = variables.detail_url_list
    # while True:
    print("get detail url started")
    time.sleep(1)
    for  i in range(20):
        detail_url_list.append("http://www.baidu.com/{id}".format(id=i))
    print("get detail url end")

#1.线程间的通信方式-共享变量
'''
线程间的变量共享涉及到很多的线程安全性问题,因为gil的原因会导致
线程在处理数据的时候没有达到预期的效果,可能会造成一定的混乱。
所以并不推荐使用共享变量进行通信,除非大家对锁比较了解

'''
#2.

if __name__=="__main__":
    thread_detail_url=threading.Thread(target=get_detail_url,args=())
    '''
    在真正爬取url列表页面的时候是很快的,而爬取每个url里的详情速度是很慢的
    如果这两个爬取方式只是分别之构建一个线程,则没有做到高并发,那么就可以
    考虑构建多个爬取详情页的线程来增加并发,提高运行效率
    '''
    '''
    直观的做法:
    thread_detail_html1 = threading.Thread(target=get_detail_html, args={"", })
    thread_detail_html2 = threading.Thread(target=get_detail_html, args={"", })
    thread_detail_html3 = threading.Thread(target=get_detail_html, args={"", })
    thread_detail_html4 = threading.Thread(target=get_detail_html, args={"", })
    thread_detail_html5 = threading.Thread(target=get_detail_html, args={"", })
    thread2 = threading.Thread(target=get_detail_url, args={"",})
    
    thread_detail_url.start()
    thread_detail_html1.start()
    thread_detail_html2.start()
    thread_detail_html3.start()
    thread_detail_html4.start()
    thread_detail_html5.start()
    thread_detail_url.join()
    thread_detail_html1.join()
    thread_detail_html2.join()
    thread_detail_html3.join()
    thread_detail_html4.join()
    thread_detail_html5.join()
    '''
    #其实可以使用循环来做
    for i in range(10):
        detail_html_thread=threading.Thread(target=get_detail_html,args=())
    #设置子进程为守护线程  当主线程退出的时候,子线程也会被关掉。
    # thread_detail_url.setDaemon(True)
    # detail_html_thread.setDaemon(True)

    # start_time=time.time()
    thread_detail_url.start()
    detail_html_thread.start()

    #那么如何使得在两个线程运行完成之后再执行主线程呢
    thread_detail_url.join()
    detail_html_thread.join()


2.通过queue进行线程间同步

#   示意代码,不可运行。
#通过queue的方式来进行线程间的同步的
import time
import threading

from queue import  Queue
#这里是线程安全的,为什么呢,是因为queue本身就是一个线程安全的
#在queue.get()在进行get操作的时候,他不会使得我们多个我线程去、
#操作同一个queue的时候造成数据的错误

'''
Queue是如何做到线程安全的呢,如里面的get方法是线程安全的,底层用到了
deque双端队列,它是线程安全的
'''

# 爬取文章详情页
def get_detail_html(queue):
        #get方法是个阻塞的方法 如果队列为空,他会一直停在这
        url = queue.get()
        # for url in detail_url_list:
        print("get detail html started")
        time.sleep(0.5)
        print("get detail html end")


# 爬取文章列表页
def get_detail_url():
    while True:
        print("get detail url started")
        time.sleep(1)
        for i in range(20):
            Queue.put("http://www.baidu.com/{id}".format(id=i))
        print("get detail url end")


if __name__ == "__main__":
    detail_url_queue=Queue(maxsize=1000)

    thread_detail_url = threading.Thread(target=get_detail_url, args=(detail_url_queue,))
    for i in range(10):
        detail_html_thread = threading.Thread(target=get_detail_html, args=(detail_url_queue,))
    # 设置子进程为守护线程  当主线程退出的时候,子线程也会被关掉。
    # thread_detail_url.setDaemon(True)
    # detail_html_thread.setDaemon(True)

    # start_time=time.time()
    thread_detail_url.start()
    detail_html_thread.start()

    # 那么如何使得在两个线程运行完成之后再执行主线程呢
    # thread_detail_url.join()
    # detail_html_thread.join()

    detail_url_queue.task_done()
    detail_url_queue.join()


线程同步(Lock,RLock,Semaphores、Condition)

python中为什么需要线程同步,以及同步是什么意思?

import threading
from threading import Lock


total=0

lock=Lock()
def add():
    global total
    global lock
    for i in range(1000000):
        #获取一把锁
        lock.acquire()
        lock.acquire()
        #不能在一把锁没有释放的情况下,又获取锁
        #造成死锁
        total+=1
        #释放锁

        lock.release()
def  desc():
    global  total
    for i in range(1000000):
        #获取一把锁
        lock.acquire()
        total-=1
        #释放锁
        lock.release()
thread1=threading.Thread(target=add)
thread2=threading.Thread(target=desc)
thread1.start()
thread2.start()

'''
线程同步机制:
1.

'''

# def add1(a):
#     a+=1
# def desc1(a):
#     a-=1
# import dis
# print(dis.dis(add1))
# print(dis.dis(desc1))
'''
add
1.load a     a=0
2.load 1     1
3.+     1
4.赋值给a    1

desc
1.load a     a=0 
2.load 1
3.-     -1     
4.赋值给a     -1
在这个字节码过程当中的任意一个过程,
线程都有可能被切换出去给另一个线程.



使用了锁之后,多线程之间谁先获取锁,那么这个线程就
会先执行自己的代码,并且如果这个线程的锁如果没有在
执行后得到释放,那么所有其他所有线程都会被卡住。

1.使用了锁之后会影响我们的性能,获取锁和释放锁都需
要时间。  这是锁会影响并发性能的必然问题。
2.用锁会引起死锁。非常容易引起死锁。(小心)
  死锁的情况:A(a,b)
  A(a,b)
  首先 acquire (a)
       acquire (b)
  B(a,b)
  acquire (b)
  acquire(a)
  这种情况下造成锁资源的竞争。
  当A线程在和B先程
  
'''

thread1.join()
thread2.join()
print(total)



#RLock在同一个线程里面,可以连续调用多次acquire,
#一定要注意acquire的次数要和release的次数相等

Condition :
在使用从第同之前先使用lock实验下面的场景,达不到说一句回答一句的
聊天效果。实例代码如下:

from threading import Condition
#Condition条件变量,用于复杂的线程间同步的锁。
from threading import  Lock
import threading
class XiaoAi(threading.Thread):
    def __init__(self,lock):
        self.lock=lock

        super().__init__(name="小爱")
    def run(self):
        self.lock.acquire()
        print("{}: 在".format(self.name))
        self.lock.release()
        self.lock.acquire()
        print("{}: 好啊。".format(self.name))
        self.lock.release()
        pass
class TianMaoJingLing(threading.Thread):
    def __init__(self,lock):
        self.lock=lock
        super().__init__(name="天猫精灵")
    def run(self):
       self.lock.acquire()
       print("{}: 小爱同学".format(self.name))
       self.lock.release()
       self.lock.acquire()
       print("{}: 我们来对古诗吧".format(self.name))
       self.lock.release()
if  __name__=="__main__":
     lock=Lock()
     xiaoai=XiaoAi(lock)
     tianmao=TianMaoJingLing(lock)
     tianmao.start()
     xiaoai.start()


使用Condition来实现聊天场景

from threading import Condition
#Condition条件变量,用于复杂的线程间同步的锁。
from threading import  Lock
import threading
# class XiaoAi(threading.Thread):
#     def __init__(self,lock):
#         self.lock=lock
#
#         super().__init__(name="小爱")
#     def run(self):
#         self.lock.acquire()
#         print("{}: 在".format(self.name))
#         self.lock.release()
#         self.lock.acquire()
#         print("{}: 好啊。".format(self.name))
#         self.lock.release()
#         pass
# class TianMaoJingLing(threading.Thread):
#     def __init__(self,lock):
#         self.lock=lock
#         super().__init__(name="天猫精灵")
#     def run(self):
#        self.lock.acquire()
#        print("{}: 小爱同学".format(self.name))
#        self.lock.release()
#        self.lock.acquire()
#        print("{}: 我们来对古诗吧".format(self.name))
#        self.lock.release()
# if  __name__=="__main__":
#      lock=Lock()
#      xiaoai=XiaoAi(lock)
#      tianmao=TianMaoJingLing(lock)
#      tianmao.start()
#      xiaoai.start()

from threading import Condition
#通过condition完成协同对话
class XiaoAi(threading.Thread):
    def __init__(self,cond):
        self.cond=cond
        super().__init__(name="小爱")
    def run(self):
        # with self.cond:
            self.cond.acquire()
            #等待通知
            self.cond.wait()
            print("{}: 在".format(self.name))
            #通知天猫精灵
            self.cond.notify()

            #等待通知
            self.cond.wait()
            print("{}: 好啊.".format(self.name))
            #通知天猫精灵
            self.cond.notify()

            #等待通知
            self.cond.wait()
            print("{}: 君住长江尾.".format(self.name))
            #通知天猫精灵
            self.cond.notify()

            #等待通知
            self.cond.wait()
            print("{}: 共饮长江水.".format(self.name))
            #通知天猫精灵
            self.cond.notify()

            #等待通知
            self.cond.wait()
            print("{}: 此恨何时已.".format(self.name))
            #通知天猫精灵
            self.cond.notify()

            #等待通知
            self.cond.wait()
            print("{}: 定不负相思忆.".format(self.name))
            #通知天猫精灵
            self.cond.notify()
            self.cond.release()



class TianMaoJingLing(threading.Thread):
    def __init__(self,cond):
        self.cond=cond
        super().__init__(name="天猫精灵")
    def run(self):
       with self.cond:
           print("{}: 小爱同学。".format(self.name))
           #通知小爱同学
           self.cond.notify()
           #进入等待的状态  等待小爱的通知
           self.cond.wait()

           print("{}: 我们来对古诗吧。".format(self.name))
           #通知小爱同学
           self.cond.notify()
           #进入等待的状态  等待小爱的通知
           self.cond.wait()

           print("{}: 我在长江头。".format(self.name))
           #通知小爱同学
           self.cond.notify()
           #进入等待的状态  等待小爱的通知
           self.cond.wait()

           print("{}: 日日思君不见君。".format(self.name))
           #通知小爱同学
           self.cond.notify()
           #进入等待的状态  等待小爱的通知
           self.cond.wait()

           print("{}: 此水几时休。".format(self.name))
           #通知小爱同学
           self.cond.notify()
           #进入等待的状态  等待小爱的通知
           self.cond.wait()

           print("{}: 只愿君心似我心。".format(self.name))
           #通知小爱同学
           self.cond.notify()
           #进入等待的状态  等待小爱的通知
           self.cond.wait()



if  __name__=="__main__":
     cond=Condition()
     xiaoai=XiaoAi(cond)
     tianmao=TianMaoJingLing(cond)
     #启动顺序很重要
     #在调用with cond 之后才能调用 wait()或者notify()
     #condition有两层锁一把底层锁会在线程调用了wait方法
     # 的时候释放掉,上面的锁会在每次调用wait的时候分配
     #一把锁并放到cond的等待队列中,等待notify方法的唤醒
     
     xiaoai.start()
     tianmao.start()


运行结果如下图:
在这里插入图片描述

wait()函数允许我们等待某个条件变量的通知

semaphore 是用于控制进入数量的锁

#semphore  是用于控制进入数量的锁

#文件, 读,写,写一般只是用于一个线程写,读可以允许有多个线程读
#爬虫
import threading
import time
class HtmlSpider(threading.Thread):
    def __init__(self,url,sem):
        super().__init__()
        self.url=url
        self.sem=sem
    def run(self):
        time.sleep(2)
        print("got html text success")
        self.sem.release()
class UrlProducer(threading.Thread):
    def __init__(self,sem):
        super().__init__(name="")
        self.sem=sem
    def run(self):
        for i in range(20):
            self.sem.acquire()
            html_thread=HtmlSpider("http://www.baidu.com/{}".format(i),self.sem)
            html_thread.start()
if __name__=="__main__":
    #控制进入某段代码的线程数量 即每次建立3个线程
    sem=threading.Semaphore(3)
    url_producer=UrlProducer(sem)
    url_producer.start()
'''
输出结果每次输出三个结果。
'''
    from queue import Queue

concurrent线程池编程

python3.2+

from concurrent import futures
#线程池   为什么要线程池
'''
能不能有个线程管理包去管理我们的线程,
第一控制线程进入的数量

在主线程中可以获取某某一个线程的状态或者说某一个任务
的状态以及返回值
当一个线程完成的时候我们主线程能够立即知道
futures可以让多线程和多进程编码接口一致


'''
from concurrent.futures import ThreadPoolExecutor,as_completed,wait,FIRST_COMPLETED
                                                   #wait可以让我们的主线程阻塞
import time
def  get_html(times):
    time.sleep(times)
    print("get page {} success".format(times))
    return times
#实例化线程池
executor=ThreadPoolExecutor(max_workers=2)
#通过submit函数提交执行的函数到线程池中  submit是立即返回是非阻塞的
# task1=executor.submit(get_html,(3))
# task2=executor.submit(get_html,2)

'''
在实际的爬虫当中,我们并不是获取所有的线程的状态,
而是要获取已经成功的task的返回
'''
urls=[3,2,4]
#1批量提交
all_task=[executor.submit(get_html,(url)) for url in urls]
# wait(all_task)
#这样的话,我们就可以控制主线程等待某件事情的发生之后再继续执行
wait(all_task,return_when=FIRST_COMPLETED)
print("main")

#通过as_completed获取成已完成的task的结果1
# for future in as_completed(all_task):
#     data=future.result()
#     print("get {} page success".format(data))

#2通过executor的map获取已经完成的task的结果
# for result in executor.map(get_html,urls):
#     print("get {} page".format(result))







# #done()用于判定某个任务是否完成
# print(task1.done())
# #取消某一个线程
# print(task2.cancel())
# time.sleep(5)
# #是一个阻塞的方法,它能够返回一个结果
# print(task1.done())
# #result可以获取task的执行结果
# print(task1.result())






from concurrent.futures import Future
‘’’
Future == task的返回容器或者未来对象 这一设计理念比较重要
是因为在submit()返回的对象线程任务所处的
状态有可能是未完成的,但是有可能在将来某个时候完成
所以说他是我们异步的核心。

多进程

因为有一把gil锁的存在,python中的多线程实际上无法利用我们多核的优势,无法达到并行的优势,多进程编程就可以利用我们多个cpu并发的这么个需求去提高我们的运行效率。

多进程 与 多线程:
1.对于耗cpu的操作,使用多进程.
2.对于io的操作,使用多线程.切换进程的代价要高于线程

对于耗费cpu的操作,计算,图形处理,机器学习的算法,以及流行的比特币挖矿等等耗费到cpu已经不能够满足计算的需求,所以说现在的显卡里面的GPU它的运算速度是高于CPU的 。 对于耗费cpu的操作,多进程优于多线程。

#多cpu的操作用多进程编程
#1.对于耗cpu的操作,使用多进程。
# 2.对于io的操作,使用多线程.切换进程的代价要高于线程。

'''

多进程和多线程使用concurrent.futures的效率是差不多的。
'''

def  fibo(n):
    if n<2:
        return 1
    return fibo(n-1)+fibo(n-2)
# print(fibo(10))#89
# 1.使用多线程
from concurrent.futures import ThreadPoolExecutor,as_completed
import time
# with ThreadPoolExecutor(3) as excutor:
#     alltask=[excutor.submit(fibo,(num)) for num in range(25,35)]
#     starttime=time.time()
#     for future in as_completed(alltask):
#         data=future.result()
#         print("get numbers results is:  {}".format(data))
#     print("last time is:{}".format(time.time()-starttime))
'''
89
get numbers results is:  121393
get numbers results is:  196418
get numbers results is:  317811
get numbers results is:  514229
get numbers results is:  832040
get numbers results is:  1346269
get numbers results is:  2178309
get numbers results is:  3524578
get numbers results is:  5702887
get numbers results is:  9227465
last time is:15.730899810791016

'''
#2.多进程
import time
from  concurrent.futures import ProcessPoolExecutor
'''
注意:windows多进程编程时一定要将执行的ProcessPoolExecutor代码放到
if  __name__=="__main__":之下,不然会报错。
'''
if __name__=="__main__":
    with ProcessPoolExecutor(3) as excutor:
        alltask = [excutor.submit(fibo, (num)) for num in range(25, 45)]
        starttime = time.time()
        for future in as_completed(alltask):
            data = future.result()
            print("get numbers results is:  {}".format(data))
        print("last time is:{}".format(time.time() - starttime))
'''
H:\ANACONDA\python.exe I:/ainlp/pythonHight/chapter11/progress_test.py
get numbers results is:  121393
get numbers results is:  196418
get numbers results is:  317811
get numbers results is:  514229
get numbers results is:  832040
get numbers results is:  1346269
get numbers results is:  2178309
get numbers results is:  3524578
get numbers results is:  5702887
get numbers results is:  9227465
last time is:9.456540822982788
'''

对于io操作来说,多线程优于我们的多进程:

#对于io操作来说,多线程优于我们的多进程
def random_sleep(n):
    time.sleep(n)
    return n
# if __name__=="__main__":
#     with ThreadPoolExecutor(3) as excutor:
#         alltask = [excutor.submit(random_sleep, (num)) for num in [2]*30]
#         starttime = time.time()
#         for future in as_completed(alltask):
#             data = future.result()
#             print("get numbers results is:  {}".format(data))
#         print("last time is:{}".format(time.time() - starttime))
'''
H:\ANACONDA\python.exe I:/ainlp/pythonHight/chapter11/progress_test.py
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
last time is:20.00114417076111
'''
if __name__=="__main__":
    with ProcessPoolExecutor(3) as excutor:
        alltask = [excutor.submit(random_sleep, (num)) for num in [2]*30]
        starttime = time.time()
        for future in as_completed(alltask):
            data = future.result()
            print("get numbers results is:  {}".format(data))
        print("last time is:{}".format(time.time() - starttime))
'''
H:\ANACONDA\python.exe I:/ainlp/pythonHight/chapter11/progress_test.py
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
get numbers results is:  2
last time is:20.690183401107788
'''

其实在计算量不是很大的时候,多线程和多进程性能差别不大。操作系统调用的线程数比进程数要多很多,调用和切换进程会消耗很多的cpu内存,因此在某些情况下使用多线程的性能是不会比多进程差的,而且多线程的稳定性是比多进程好的。

多进程编程:

先看一下这个东西:

import  os
import time
#fork只能用于linux/unix上

pid  =os.fork()
print("xiaopang")
if pid==0:
    print('子进程 {} ,父进程是: {}.'.format(os.getpid(),os.getppid()))
else:
    print('我是父进程 : {}'.format(pid))
time.sleep(2)

运行结果:
在这里插入图片描述
为什么两个进程的fpid不同呢,这与fork函数的特性有关。fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;

将print("xiaopang ")放在fok()的前面。

import  os
import time
#fork只能用于linux/unix上
print("xiaopang")
pid  =os.fork()
if pid==0:
    print('子进程 {} ,父进程是: {}.'.format(os.getpid(),os.getppid()))
else:
    print('我是父进程 : {}'.format(pid))
time.sleep(2)

在这里插入图片描述

多进程和进程池

from concurrent.futures import ProcessPoolExecutor
import multiprocessing


#多进程编程
import  time
def  get_html(n):
   time.sleep(n)
   print("sub_progress success")
   return  n


if   __name__=="__main__":
    # process=multiprocessing.Process(target=get_html,args=(2,))
    # print(process.pid)
    # process.start()
    # print(process.pid)
    # process.join()
    # print("main progress end")


    # #使用进程池
    pool=multiprocessing.Pool(multiprocessing.cpu_count())
    # result=pool.apply_async(get_html,args=(3,))
    # #等待所有任务完成
    # pool.close()
    # pool.join()
    # print(result.get())
    #imap
    # for result in pool.imap(get_html,[1,5,3]):
    #     print("{} sleep success".format(result))
    #imap_unordered
    for result in pool.imap_unordered(get_html,[1,5,3]):
        print("{} sleep success".format(result))

多进程之间的通信

1使用multiprogressing 中的queue在进程之间进行通信

from multiprocessing import  Process,Queue

import  time
def producer(queue):
    queue.put("a")
    time.sleep(2)
def cusumer(queue):
    time.sleep(2)
    data=queue.get()
    print(data)
if __name__=="__main__":
    queue=Queue(10)
    myproducer=Process(target=producer,args=(queue,))
    mycusumer=Process(target=cusumer,args=(queue,))
    myproducer.start()
    mycusumer.start()
    myproducer.join()
    mycusumer.join()

multiprocessing中的queue不能用于进程池的通讯,因为
不起作用。

#multiprocessing中的queue不能用于进程池的通讯
from multiprocessing import  Process,Queue,Pool

import  time
def producer(queue):
    queue.put("a")
    time.sleep(2)
def cusumer(queue):
    time.sleep(2)
    data=queue.get()
    print(data)
if __name__=="__main__":
    queue=Queue(10)
    pool=Pool(2)
    pool.apply_async(producer,args=(queue,))
    pool.apply_async(cusumer,args=(queue,))
    pool.close()
    pool.join()

可以使用Manage之下的queue进行进程池间的通信

from multiprocessing import  Process,Queue,Pool,Manager

import  time
def producer(queue):
    queue.put("a")
    time.sleep(2)
def cusumer(queue):
    time.sleep(2)
    data=queue.get()
    print(data)
if __name__=="__main__":
    queue=Manager().Queue(10)
    pool=Pool(2)
    pool.apply_async(producer,args=(queue,))
    pool.apply_async(cusumer,args=(queue,))
    pool.close()
    pool.join()

进程间通讯的方式 管道pipe
pipe的性能高于queue
pipe只能用于两个进程间的通讯



#进程间通讯的方式  管道pipe
# pipe的性能高于queue
# pipe只能用于两个进程间的通讯
from multiprocessing import  Process,Pipe
                                #Pipe是简化版的queue
import  time
def producer(pipe):
    pipe.send("xiaopang")

def cusumer(pipe):
    print(pipe.recv())


if __name__=="__main__":
    recevie_pipe,send_pipe=Pipe()
    # pipe只能用于两个进程间的通讯
    myproducer=Process(target=producer,args=(send_pipe,))
    mycusumer=Process(target=cusumer,args=(recevie_pipe,))

    myproducer.start()
    mycusumer.start()
    myproducer.join()
    mycusumer.join()


使用Manage里的dict进行进程间的共享内存操作,注意数据间的进程同步.

# 使用Manage里的dict进行进程间的共享内存操作
from multiprocessing import Manager,Process
def add_data(p_dict,key,value):
    p_dict[key]=value
if __name__=="__main__":
    process_dict=Manager().dict()
    first_Process=Process(target=add_data,args=(process_dict,"xiaopang ",23))
    second_process=Process(target=add_data,args=(process_dict,"xiaohe",26))
    first_Process.start()
    second_process.start()
    first_Process.join()
    second_process.join()
    print(process_dict)


完结

下一篇: 协程和异步io

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章