Python程序员都知道的入门知识の八

作者: 耑意儿 | 来源:发表于2017-01-04 18:59 被阅读184次
    目录:
    1. 多线程threading、Queue
    2. 多进程multiprocessing
    3. 小知识
    
    目录【Python程序员都知道的入门知识】

    python自学之路

    1. 多线程threading、Queue

    Python的多线程由threading和Queue来实现。
    threading模块:用于创建和管理线程
    Queue模块:用于实现线程间通讯和数据共享

    1.1. threading

    1.1.1. 概述threading

    threading中包含的类:
    Thread:线程的基本操作
    Lock&RLock:多线程的互斥锁,实现线程同步
    Event:用以协调线程的运行,实现线程间通信
    Semaphore:用以确保一定资源多线程访问时的上限
    Condition:用以实现复杂同步

    threading的常用内置函数:
    threading.currentThread():获取当前线程对象
    threading.activeCount():获取当前正在运行的线程数量
    threading.enumerate():获取当前正在运行的线程列表
    threading.Lock():获取线程同步锁

    1.1.2. threading.Thread

    threading.Thread的常用内置函数:
    start():启动线程的方法
    run():线程运行的方法
    setName():设置线程名
    getName():获取线程名
    isAlive():判断线程是否还是活跃状态
    join([time]):等待线程终止
    setDaemon(bool):守护线程,设置子线程是否随主线程一起销毁,默认为False
    isDaemon():判断是否为守护线程

    那么问题来了,怎么用Thread?有以下两种方式:
    1、直接获取对象:

    • 导入threading模块
    • 实例化对象:
      mythread = threading.Thread(group=None, target=None, name=None, args=(), kwargs={})
      <a>留意参数:</a>
      group:必须为none,保留参数
      target:当run方法被执行时调用的对象
      name:线程名
      args:target可能需要调用到的元组参数
      kwangs:target可能需要调用到的字典参数
    • 启动线程:mythread.start()

    2、继承threading.Thread:

    • 导入threading模块
    • 类继承threading.Thread类
    • 重写__init__()方法和run()方法
      <a>注意:</a>重写父类构造函数__init__()的时候首先要显式调用父类的构造函数:Thread.__init__()
    Python官方文档说明

    1.1.3. Lock的内置函数

    acquire():上锁
    release():释放锁

    1.2 使用Queue队列

    • 导入Queue模块
    • 获取对象:Queue.Queue([maxsize]),当maxsize值小于1的时候表示容量无限大。

    Queue的内置函数:
    put(item,[block ,[timeout]]):往队列中写入内容,如果队列已满线程会进入等待,直到队列空出位置。
    put_nowait(item):不等待直接写入内容,如果队列已满则直接抛出异常
    get(block ,[timeout]]):获取队列内容,如果队列为空,线程会一直等到写入内容
    get_nowait():不等待直接获取内容。如果队列内容为空会抛出异常,不让线程等待

    <a>注意:</a>

    • block参数表示当队列已满或为空时是让线程等待还是直接抛出异常,1为暂停,默认值;0是直接抛出异常
    • 如果不希望在队列已满或队列为空时抛出异常,又不希望让线程等待太久,可以设置timeout,线程等待时间。此时block要同时手动设置为1
    • timeout超时时间单位为秒,不同于java为毫秒
    Paste_Image.png

    如果不想抛出异常或线程等待,操作前就做个判断吧:
    qsize():获取队列已写入内容的数量
    empty():判断队列是否有写入内容
    full():判断队列是否已满

    Paste_Image.png

    join():等到队列为空时再作其他操作
    task_done():发送任务完成的信号给阻塞的队列,调用次数要与放入队列的数据个数相等。
    如下图所示,如果将箭头所指向的那两句self.q.task_done()都注释掉或者只注释掉其中一句都会出现像第一次运行结果那样无法打印出complete,程序也不会运行结束。

    Paste_Image.png

    队列的三种类型:

    • 先进先出FIFO:Queue
    • 后进先出LIFO:LifoQueue
    • 优先级队列:PriorityQueue

    1.3. Demo

    从网上摘抄了一个生产者与消费者的demo,并做了一些小修改(留意注释部分):

    # -*- coding: cp936 -*-
    import time
    import threading
    import Queue
    import urllib2
    
    threadLock = threading.Lock()
    
    class Consumer(threading.Thread):
        def __init__(self,queue):
            threading.Thread.__init__(self)
            self._queue = queue
    
        def run(self):
            # 尝试在执行run方法时加入线程锁,试试看运行后的效果有什么差别
            # threadLock.acquire()
            print 'do run method'
            while True:
                content = self._queue.get()
                # 试试看休眠下会有什么影响
                # time.sleep(1)
                if isinstance(content,str) and content == 'quit':
                    print ('content equals to quit',self.getName())
                    break
                response = urllib2.urlopen(content)
                print ('open url',content,self.getName())
            print ('Bye byes!')
            # 上线程锁后,记得释放锁
            # threadLock.release()
            
    
    
    def Producer():
        urls = [
            'http://www.python.org','http://www.baidu.com',
            'http://www.jianshu.com','http://www.tianmao.com'
        ]
        queue = Queue.Queue()
        worker_threads = build_worker_pool(queue,4)
        start_time = time.time()
    
        for url in urls:
            queue.put(url)
            queue.put('quit')
        # 试试看把 queue.put('quit')放到放到独立的循环里会有什么影响:
        # for worker in worker_threads:
        #    queue.put('quit')
        for worker in worker_threads:
            worker.join()
    
    
        print 'Done Time taken:{}'.format(time.time() - start_time)
    
    
    # 创建线程池,一个线程就是一个消费者
    def build_worker_pool(queue,size):
        workers = []
        for _ in range(size):
            worker = Consumer(queue)
            worker.start()
            workers.append(worker)
        return workers
    
    if __name__ == '__main__':
        Producer()
    
    

    2. 多进程multiprocessing

    导入模块:multiprocessing
    获取对象:multiprocessing.Process()

    2.1. 概述多进程

    多进程与多线程使用相似的API:

    • multiprocessing包与threading一样,有Lock、Event、Condition、Semaphore等类
    • multiprocessing.Process类与threading.Thread的用法相同。

    多进程与多线程的差别:

    • 每个进程对象应调用join()方法以避免出现僵尸进程
    • 独立的一套进程间通讯方式(IPC):Pipe,Queue
    • 不同于多线程,多进程应避免共享资源

    2.2. multiprocessing.Pipe实现IPC

    <a>Pipe实现的是两个进程间的通讯</a>
    导入模块:import multiprocessing
    获取对象:
    multiprocessing.Pipe([False])
    (加参数False时获取到的是单向管道对象)
    往管道一端发送数据:send()
    往管道另一端接收数据:recv()

    2.3. multiprocessing.Queue实现IPC

    <a>Queue实现的是多个进程间的通讯</a>
    导入模块:import multiprocessing
    获取对象:
    multiprocessing.Queue([maxsize])
    (maxsize表示的是队列的最大容量)
    往进程队列中存入数据:put()
    往进程队列抓取数据:get()

    关于进程间通讯(IPC),网上找的这篇感觉还不错,抽时间再细看看:
    进程间通讯(IPC)作者: ZH奶酪——张贺

    3. 小知识

    3.1. 给模块重命名

    如果觉得multiprocessing太长,可以在导入模块的时候对其进行重命名,如:
    import multiprocessing as mp

    3.2. 理解:if name == 'main'

    当当前模块被直接运行时,条件成立
    当当前模块作为被导入模块时,条件不成立

    3.3. xrange 与 range的区别

    都是在一定范围内生成序列,但是xrange的性能会优于range
    xrange不会像range一样立即生成一个list列表,而是先创建一个生成器(即xrange对象)。
    xrange在创建成功后不会立即就开辟空间。

    Paste_Image.png

    3.4. 什么是守护线程

    如果设置为守护线程,那么主线程结束会直接将其他线程结束掉,
    如果不是守护线程,那么主线程结束后会等线程执行完毕。

    相关文章

      网友评论

        本文标题:Python程序员都知道的入门知识の八

        本文链接:https://www.haomeiwen.com/subject/fxhnvttx.html