• 多进程介绍


    实验发现不涉及IO输入的多线程,串行运行在老的(新解释器缩短差距)解释器有时候比多线程快,这是什么原因?

    GIL:全局解释锁(这玩意跟python语言无关,跟解释种类有关,只对CPython解释器有用,但是这种站主导市场)
           因为有GIL,所以同一时刻,只有一个线程被一个CPU执行
            多核对于Python多线程用不上,完了!!!
            多进程可以用上多核,但是进程的开销大!!

    总结:多线程用不上多核(计算密集型任务时,不如串行;IO密集型任务它还是不错的),多进程太多时开销大,所以用多进程+协程。

     1 #!/usr/bin/env python
     2 #-*-coding:utf-8 -*-
     3 
     4 '''
     5 python 大都编辑器的单一进程中的多线程是无法用多核的,因为GIL;但是多进程可以,所以多进程模块multiprocessing
     6 线程的调用两种方法,一是直接用treading模块,二是继承这个模块。进程相似(守护进程deamon有点区别)
     7 如果有4个CPU,开4个进程,就可以实现真的并行,而不是并发
     8 '''
     9 
    10 import time, multiprocessing
    11 import threading
    12 
    13 def f(name):
    14     time.sleep(1)
    15     print("hello",name,time.ctime())
    16 
    17 if __name__ == '__main__':
    18     plist = []
    19     for i in range(2):
    20         p = multiprocessing.Process(target=f,args=("wan",))
    21         # p = threading.Thread(target=f,args=('wan',))
    22         plist.append(p)
    23         p.start()
    24 
    25     for i in plist:
    26         i.join()
    27 
    28     print("ending........")
     1 from multiprocessing import Process
     2 import os
     3 import time
     4 
     5 
     6 def info(title):
     7     print("title:", title)
     8     print('parent process:', os.getppid())  # 每个进程有一个ID号,getppid是得到父进程id
     9     print('process id:', os.getpid())       # getpid是得到自己当前运行的id
    10 
    11 def f(name):
    12 
    13     info('function f')
    14     print('hello', name)
    15 
    16 
    17 if __name__ == '__main__':
    18 
    19     info('main process line')
    20 
    21     time.sleep(1)
    22     print("------------------")
    23     p = Process(target=info, args=('yuan',))     # 这里的父进程的ID号应该是等于上面info调用的后等到的process id 一样
    24     p.start()
    25     p.join()
    View Code

     介绍一下multiprocessing.Process类:

            is_alive() 返回进程是否运行

            terminate () 不管任务是否完成,立即停止工作进程

    进程之间的通讯,因为一个进程中的多个线程是在同一块进程的内存空间中,他们数据共享,很简单。但是进程之间的内存空间不同,要解决进程之间的通讯,以及共享数据问题(我改了数据,你拿到的应该是我改后的)。进程中涉及大量的数据拷贝,所以资源消耗大

    下面讲进程通讯:队列,管道(前两个只是完成通讯功能,没有实现数据共享),Managers(可实现数据共享)

    队列:

             说队列前,先介绍一下列表这种数据结构,看看它在线程中的缺陷,线程不安全。

     1 import threading,time
     2 
     3 li = [1, 2, 3, 4, 5]
     4 
     5 def pri():    # 每个线程删除列表中的最后一个元素
     6     while li:
     7         a = li[-1]
     8         print(a)
     9         time.sleep(1)
    10         li.remove(a)
    11 
    12 t1 = threading.Thread(target=pri)
    13 t2 = threading.Thread(target=pri)
    14 
    15 t1.start()
    16 t2.start()
    17 
    18 t1.join()
    19 t2.join()
    20 print("end.............")
    View Code

           用了队列,刚才列表在线程中的问题就不存在了。

     1 import queue
     2 
     3 q = queue.Queue(3) # 先进先出,后进先出是.LifeoQueue,还可以按级别出.PriorityQueue
     4 
     5 q.put(12)          # put 与 get还有一些其他的参数
     6 q.put("hello")
     7 q.put({'name':"wan"})
     8 
     9 print(q.qsize())       # 队列中实际有多少值
    10 print(q.empty())       # 是否为空
    11 print(q.full())        # 是否为满
    12 
    13 while 1:
    14     data = q.get()
    15     print(data)
    16     print("--------------")
    View Code

          同样,用队列进行进程中的通讯如下:

     1 import multiprocessing
     2 # import queue
     3 
     4 
     5 def foo(q):
     6 
     7     q.put(123)
     8     q.put("wan")
     9 
    10 if __name__ == '__main__':
    11     q = multiprocessing.Queue()               # 进程中有自己的队列
    12     plist = []
    13     for i in range(3):
    14         p = multiprocessing.Process(target=foo,args=(q,))
    15         # 这里必须把参数q传进去,因为它不像线程那样共享,在主线程建立q,只是主进程内存空间中,子进程中队列都没定义
    16         plist.append(p)
    17         p.start()
    18     print(q.get())
    19     print(q.get())
    20     print(q.get())
    21     print(q.get())
    multiprocessing.Queue

    管道通讯:

     1 from multiprocessing import Process, Pipe
     2 def f(conn):
     3     conn.send([12, {"name":"yuan"}, 'hello'])       # 子进程发
     4     response=conn.recv()                            # 子进程收
     5     print("response",response)
     6     conn.close()
     7     # print("q_ID2:",id(conn))
     8 
     9 if __name__ == '__main__':
    10 
    11     parent_conn, child_conn = Pipe()                 # 双向管道,类似电话的两头,一头给子进程,一头给主进程
    12 
    13     # print("q_ID1:",id(child_conn))
    14     p = Process(target=f, args=(child_conn,))
    15     p.start()
    16 
    17     print(parent_conn.recv())   # 父进程(主进程)接收到的
    18     parent_conn.send("儿子你好!") # 父进程发
    19     p.join()
    multiprocessing.Pipe

    Manager:

     1 from multiprocessing import Process, Manager
     2 
     3 def f(d, l,n):
     4 
     5     d[n] = '1'    #{0:"1"}
     6     d['2'] = 2    #{0:"1","2":2}
     7 
     8     l.append(n)    #[0,1,2,3,4,   0,1,2,3,4,5,6,7,8,9]
     9     #print(l)
    10 
    11 
    12 if __name__ == '__main__':
    13 
    14     with Manager() as manager:        # 类似用with打开文件,省略关的功能
    15 
    16         d = manager.dict()            #{}
    17 
    18         l = manager.list(range(5))     #[0,1,2,3,4]
    19 
    20 
    21         p_list = []
    22 
    23         for i in range(10):
    24             p = Process(target=f, args=(d,l,i))
    25             p.start()
    26             p_list.append(p)
    27 
    28         for res in p_list:
    29             res.join()
    30 
    31         print(d)
    32         print(l)
    View Code
    #!/usr/bin/env python
    #-*-coding:utf-8 -*-
    
    '''
    线程有同步(同步锁):因为它们共享数据,可能会出错;
    进程数据不共享,但是也共享一些资源(比如屏幕(进程1与进程2可能同一时刻想同时输出在屏幕上,也就是出现串行错误))
    因此,进程也需要同步multiprocessing.Lock
    
    '''
    
    from multiprocessing import Process,Lock
    
    def f(l,i):
        l.acquire()
        print('hello world %s'%i)
        l.release()
    
    if __name__ == '__main__':
        lock = Lock()
    
        for num in range(12):
            Process(target=f,args=(lock, num)).start()
    
        print('ending.............')
     1 '''
     2 进程池:控制开进程的最大量 multiprocessing.Pool
     3 进程池两个方法:apply同步 , apply_async异步
     4 
     5 回调函数:就是某个动作或者某个函数执行成功后再去执行的函数
     6         感觉回调函数可以把内容写在它跟的函数后面,这样基本就等同了
     7         但是callback是在主进程下面调用的
     8 '''
     9 
    10 from multiprocessing import Process,Pool
    11 import time,os
    12 
    13 def f(i):
    14     time.sleep(3)
    15     print(i)
    16     print("son pid: ",os.getpid())
    17     return 'hello %s'%i
    18 
    19 def Bar(arg):                  # 回调函数必须至少有一个参数,就是它跟的函数的返回值
    20     print(arg)
    21     print('callback pid: ',os.getpid())
    22 
    23 if __name__ == '__main__':
    24     pool = Pool(5)
    25     print("main pid:  ",os.getpid())
    26     for i in range(100):
    27         pool.apply_async(func=f,args=(i,),callback=Bar)       # callback是回调函数名字,它在每个子进程执行完后执行
    28 
    29     pool.close()
    30     pool.join()             # 注意这个pool.close与pool.join的顺序是固定的
  • 相关阅读:
    html5-特殊符号的使用
    html5-表格
    html5-列表
    html5-绝对路径/相对路径
    html5-嵌入图片
    html5-超级链接
    html5-常用的文本元素
    html5-了解元素的属性
    Scanner类throwFor(Unknown Source)及跳过下一个扫描器分析
    有关HashMap的一些问题及解答
  • 原文地址:https://www.cnblogs.com/maxiaonong/p/9538422.html
Copyright © 2020-2023  润新知