zoukankan      html  css  js  c++  java
  • Python并发实践_01_线程与进程初探

    进程与线程

    在多任务处理中,每一个任务都有自己的进程,一个任务会有很多子任务,这些在进程中开启线程来执行这些子任务。一般来说,可以将独立调度、分配的基本单元作为线程运行,而进程是资源拥有的基本单位。

    python支持多进程multiprocessing,以及多线程threading。

    多进程

    os.fork()函数可以开启一个进程。该函数会返回两次值,分别在父进程中返回子进程的ID,而在子进程中永远返回0。

    os.getpid()函数可以返回进程的ID。os.getppid()则可以返回父进程的ID。

    通过os.fork(),可以随时开启一个进程并且返回进程ID,以及使用os.getpid()、os.getppid()函数,可以任意时刻查看目前所在的进程ID,以及父进程的ID。

    fork()函数只在unix/linux下有效,windows并不支持fork(),使用multiprocessing.Process类提供的多进程调用更加完整地反映整个过程。

    from multiprocessing imort Process
    def proc_worker(args):
        pass
    def main():
        p = Process(target=proc_worker,args=(some_args,))
        p.start()
        p.join()

    Process实例指定子进程运行的函数以及相关参数,并且start()和join()方法可以控制进程的开始和等待。

    需要注意的是,由于一个进程在同一时间只进行一个任务,所以子进程调用完之后,必须调用join()方法,使其父进程等待,否则进程将会成为僵尸进程。而对于线程来说,并不是必要join()。

    进程之间也可以共享内存,但是在设计上,进程应该是资源拥有的基本单位,所以应该尽量避免进程之间共享内存,由于同步的需求,这样会降低程序的效率。

    通过锁(Lock)来实现进程之间的同步:Lock实例有两个主要的方法:acquire()、released(),一把锁只能被一个进程占用。

    进程之间可以通过Pipe和Queue通信,

    其中Pipe实例时,默认是双向通道,管道的任何一端都可以收发消息,实例化时通过指定duplex=False即可创建单向通道,实例之后会得到包含两个元素的元组,代表管道的两端:

    from multiprocessing import Pipe,Process
    def proc1(pipe):
        pipe.send('There is proc1')
        print('proc1 recv:',pipe.recv())
    def proc2(pipe):
        pipe.send('There is proc2')
        print('proc2 recv:',pipe.recv())
    def main():
        pipe = Pipe()
        p1 = Process(target=proc1,args=(pipe[0],))
        p2 = Process(target=proc2,args=(pipe[1],))
        p1.start()
        p2.start()
        p1.join()
        p2.join()

    而Queue则是一个队列,满足先进先出原则(Pipe也是先进先出结构,如果发送多条信息,则会按照先进先出的顺序接收),并且Queue支持多个进程同时传入消息,并且支持多个进程同时读取消息,在实例化时,指定一个整数来限制最大允许的进程数:

    from multiprocessing import Queue,Process
    import os
    def inQueue(queue):
        info = 'put from proc:%s'%os.getpid()
        queue.put(info)
    def outQueue(queue):
        info = queue.get()
        print('%s get a info of %s'%(os.getpid(),info))
    def main():
        queue = Queue(5)
        processes1 = []
        processes2 = []
        for i in range(10):
            process = Process(target = inQueue,args=(queue,))
            process.start()
            processes1.append(process)
        for i in range(10):
            process = Process(target = outQueue,args=(queue,))
            process.start()
            processes2.append(process)
        for proc in processes1:
            proc.join()
        queue.close()
        for proc in processes2:
            proc.join()

    虽然在Queue中遵循先进先出的原则,但是由于上述代码并没有做进程同步,如果要如实反应Queue中的情况,应该添加锁:

    from multiprocessing import Process,Queue,Lock
    import os
    def inQueue(queue,lock):
        lock.acquire()
        info = 'put from proc:%s'%os.getpid()
        print '%s put a info'%os.getpid()
        queue.put(info)
        lock.release()
    def outQueue(queue,lock):
        lock.acquire()
        info = queue.get()
        print('%s get a info of %s'%(os.getpid(),info))
        lock.release()
    def main1():
        queue = Queue(10)
        lock = Lock()
        processes1 = []
        processes2 = []
        for i in range(10):
            process = Process(target = inQueue,args=(queue,lock))
            process.start()
            processes1.append(process)
        for i in range(10):
            process = Process(target = outQueue,args=(queue,lock))
            process.start()
            processes2.append(process)
        for proc in processes1:
            proc.join()
        queue.close()
        for proc in processes2:
            proc.join()

    多线程

    多线程即一个进程中执行的任务的多个子任务,从设计上应该是独立调度和分配的基本单元。Python中多线程使用threading模块实现。

    threading和multiprocessing类似,具有一个Thread类,用来实例化线程对象,Thread实例和Process一样也具有start(),join()等方法。

    前面提到进程中最好不要有资源的交换,所以锁模型在进程中比较少用到,而在线程中就比较普遍,线程与锁模型是基本的并发模型,线程中的Lock和进程中一样,也具有acquire()方法和release()方法。

    然而,在python中,多线程并发并不能完美地执行,这是因为Python的官方解释器CPython在设计时,添加了GIL锁,任何Python线程执行之前都必须获得GIL锁,每执行100条字节码GIL锁会得到释放,所以Python中的多线程仍然是交替进行的,Python中多线程只能用到同一个CPU,如果要使用多个CPU,需要用到多进程来完成。

    但是这并不表示python中的多线程没有意义,虽然不能运用多核硬件,但是同一个任务多线程执行会比开多个进程效率要高很多

  • 相关阅读:
    百度地图API显示多个标注点带百度样式信息检索窗口的代码
    百度地图API显示多个标注点并添加百度样式检索窗口
    log4j.xml 为什么要使用SLF4J而不是Log4J
    C++编程学习52个经典网站 强力推荐
    一些User-Agent
    outlook 2003配置连接exchange server 2010报错——无法完成此操作。 与 Microsoft Exchange Server 的连接不可用。 Outlook 必须联机或连接才可完成该操作
    postfix+dovecot邮箱服务器
    Java学习之InputStream中read()与read(byte[] b)
    Java Scoket之java.io.EOFException解决方案
    java.util.zip.GZIPInputStream.readUByte,Not in GZIP format错误处理
  • 原文地址:https://www.cnblogs.com/lyon2014/p/4581400.html
Copyright © 2011-2022 走看看