zoukankan      html  css  js  c++  java
  • python-多线程处理

     

    Python多线程

    Python 提供了多个模块来支持多线程编程,包括 thread、threading 和 Queue 模块等。程序是可以使用 thread 和 threading 模块来创建与管理线程。

    thread 模块提供了基本的线程和锁定支持;而 threading 模块提供了更高级别、功能更全面的线程管理。

    使用 Queue 模块,用户可以创建一个队列数据结构,用于在多线程之间进行共享。

    在python3.0中,已经将thread改名为_thread

    Python 代码的执行是由 Python 虚拟机(又名解释器主循环)进行控制的。

    对 Python 虚拟机的访问是由全局解释器锁(GIL)控制的。这个锁就是用来保证同时只能有一个线程运行的。在多线程环境中,Python 虚拟机将按照下面所述的方式执行。

    1.设置 GIL。
    2.切换进一个线程去运行。
    3.执行下面操作之一。
      a.指定数量的字节码指令。
      b.线程主动让出控制权(可以调用 time.sleep(0)来完成)。
    4.把线程设置回睡眠状态(切换出线程)。
    5.解锁 GIL。
    6.重复上述步骤。

    当一个线程完成函数的执行时,它就会退出。另外,还可以通过调用诸如 thread.exit()之类的退出函数,或者 sys.exit()之类的退出 Python 进程的标准方法,亦或者抛出 SystemExit异常,来使线程退出。不过,你不能直接“终止”一个线程。

    python多线程支持的平台:

    绝大多数类 UNIX 平台(如 Linux、Solaris、Mac OS X、*BSD 等),以及Windows 平台。

    Python 使用兼容 POSIX 的线程,也就是pthread,点击跳转百度百科

    不使用线程的情况

    我们将使用 time.sleep()函数来演示线程是如何工作的

    创建两个时间循环:一个睡眠 4 秒(loop0());另一个睡眠 2 秒(loop1()) (这里使用“loop0”和“loop1”作为函数名,暗示我们最终会有一个循环序列)。

    import time
    
    def loop0():
        print("start loop 0 at: %s" % time.ctime())
        time.sleep(4)
        print("loop 0 done at: %s" % time.ctime())
    
    
    def loop1():
        print("start loop 1 at: %s" % time.ctime())
        time.sleep(2)
        print("loop 1 done at: %s" % time.ctime())
    
    
    def main():
        print("starting at: %s" % time.ctime())
        loop0()
        loop1()
        print("all DONE at: %s" % time.ctime())
    
    
    if __name__ == '__main__':
        main()
    
    starting at: Tue Jan 23 16:03:21 2018
    start loop 0 at: Tue Jan 23 16:03:21 2018
    loop 0 done at: Tue Jan 23 16:03:25 2018
    start loop 1 at: Tue Jan 23 16:03:25 2018
    loop 1 done at: Tue Jan 23 16:03:27 2018
    all DONE at: Tue Jan 23 16:03:27 2018
    
    从输出中我们可以看出,输出整整花了我们7秒钟的时间
    单线程的输出结果

    _thread模块-一个不建议使用的模块

    _thread模块和锁对象

    thread 模块的核心函数是 start_new_thread()。它的参数包括函数(对象)、函数的参数以及可选的关键字参数。

    start_new_thread()必须包含开始的两个参数,于是即使要执行的函数不需要参数,也需要传递一个空元组。

    使用_thread模块进行编程:

    我们只需要将上面的代码进行稍微改动一下即可

    import time
    import _thread
    
    
    def loop0():
        print("start loop 0 at: %s" % time.ctime())
        time.sleep(4)
        print("loop 0 done at: %s" % time.ctime())
    
    
    def loop1():
        print("start loop 1 at: %s" % time.ctime())
        time.sleep(2)
        print("loop 1 done at: %s" % time.ctime())
    
    
    def main():
        print("starting at: %s" % time.ctime())
        _thread.start_new_thread(loop0, ())
        _thread.start_new_thread(loop1, ())
        time.sleep(5)    #暂停5秒,原因是因为我们的loop0函数暂停了4秒,如果我们小于4秒会出现无法输出loop 0 done...
        print("all DONE at: %s" % time.ctime())
    
    
    if __name__ == '__main__':
        main()
    
    starting at: Tue Jan 23 16:14:19 2018
    start loop 0 at: Tue Jan 23 16:14:19 2018
    start loop 1 at: Tue Jan 23 16:14:19 2018
    loop 1 done at: Tue Jan 23 16:14:21 2018
    loop 0 done at: Tue Jan 23 16:14:23 2018
    all DONE at: Tue Jan 23 16:14:24 2018
    
    从输出结果中我们可以看到loop0和loop1同时启动了,而不会先执行完loop0在执行loop1
    _thread多线程输出结果

    守护线程

    避免使用 thread 模块的另一个原因是该模块不支持守护线程这个概念。当主线程退出时,所有子线程都将终止,不管它们是否仍在工作。如果你不希望发生这种行为,就要引入守护线程的概念了。threading 模块支持守护线程,其工作方式是:守护线程一般是一个等待客户端请求服务的服务器。如果没有客户端请求,守护线程就是空闲的。如果把一个线程设置为守护线程,就表示这个线程是不重要的,进程退出时不需要等待这个线程执行完成。如同在第 2 章中看到的那样,服务器线程远行在一个无限循环里,并且在正常情况下不会退出。如果主线程准备退出时,不需要等待某些子线程完成,就可以为这些子线程设置守护线程标记。该标记值为真时,表示该线程是不重要的,或者说该线程只是用来等待客户端请求而不做任何其他事情。要将一个线程设置为守护线程,需要在启动线程之前执行如下赋值语句:thread.daemon = True(调用 thread.setDaemon(True)的旧方法已经弃用了)。同样,要检查线程的守护状态,也只需要检查这个值即可(对比过去调用 thread.isDaemon()的方法)。一个新的子线程会继承父线程的守护标记。整个 Python 程序(可以解读为:主线程)将在所有非守护线程退出之后才退出,换句话说,就是没有剩下存活的非守护线程时。

    threading模块

    threading模块的对象

    对象

    描述

    Thread

    表示一个执行线程的对象

    Lock

    锁原语对象(和 thread 模块中的锁一样)

    RLock

    可重入锁对象,使单一线程可以(再次)获得已持有的锁(递归锁)

    Condition

    条件变量对象,使得一个线程等待另一个线程满足特定的“条件”,比如改变状态或某个数据值

    Event

    条件变量的通用版本,任意数量的线程等待某个事件的发生,在该事件发生后所有线程将被激活

    Semaphore

    为线程间共享的有限资源提供了一个“计数器”,如果没有可用资源时会被阻塞

    BoundedSemaphore

    与 Semaphore 相似,不过它不允许超过初始值

    Timer

    与 Thread 相似,不过它要在运行前等待一段时间

    Barrier

    创建一个“障碍”,必须达到指定数量的线程后才可以继续

    threading 模块的 Thread 类是主要的执行对象。它有 thread 模块中没有的很多函数。

    Thread 对象的属性和方法

    使用 Thread 类,可以有很多方法来创建线程。

    创建Thread的实例,传给它一个函数

    代码

    import threading
    import time
    
    loops = [4, 2]
    
    
    def loop(nloop, nsec):
        print("start loop %s at: %s" % (nloop, time.ctime()))
        time.sleep(nsec)
        print("loop %s done at: %s" % (nloop, time.ctime()))
    
    
    def main():
        print("starting at: %s" % time.ctime())
        threads = []
        nloops = range(len(loops))
        for i in range(0, 2):
            t = threading.Thread(target=loop, args=(i, loops[i]))
            # print(type(i), i, type(loops[i]), loops[i])
            # 这个位置可能容易晕,第一次传入args,i会=0,loops[i]会等于4,第二次循环,i=1,loops[i]=2
            # (i=0,loops[i]=4,为什么是0和4,因为i本来就=0,loops[i]里面的i=0,loops列表的第一零个本来就等于4)
            # 会传入到loop函数中
            threads.append(t)  # 将每次循环的对象加入到列表
    
        for i in nloops:
            threads[i].start()  # 开启线程
    
        for i in nloops:  # 等待
            threads[i].join()  # 线程完成
        print("all Done at: %s" % time.ctime())
    
    
    if __name__ == '__main__':
        main()
    
    starting at: Wed Jan 24 20:54:03 2018
    start loop 0 at: Wed Jan 24 20:54:03 2018
    start loop 1 at: Wed Jan 24 20:54:03 2018
    loop 1 done at: Wed Jan 24 20:54:05 2018
    loop 0 done at: Wed Jan 24 20:54:07 2018
    all Done at: Wed Jan 24 20:54:07 2018
    输出
    作者:smelond
             
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    grails框架中在使用domain的save方法保存时保存不成功
    模块的循环导入问题、包、json与pickle模块、time与datetime模块、random模块
    模块
    迭代器、生成器、函数的递归调用、二分法
    叠加多个装饰器、有参装饰器、三元表达式、列表生成式、字典生成式、匿名函数
    函数、名称空间与作用域、闭包函数、装饰器
    函数详解
    文件处理
    字符编码
    列表、元祖、字典、集合
  • 原文地址:https://www.cnblogs.com/smelond/p/8336397.html
Copyright © 2011-2022 走看看