zoukankan      html  css  js  c++  java
  • python中的多线程

    线程和进程

    进程是资源分配的最小单位。每个进程都拥有自己的地址空间、内存、数据栈以及其他用于追踪执行的辅助数据。操作系统管理其上所有进程的执行,并为这些进程合理地分配时间。进程也可以通过派生(fork或spawn)新的进程来执行其他任务,不过因为每个新进程也都拥有自己的内存和数据栈等,所以只能采用进程间通信(IPC)的方式共享信息
    线程是程序执行的最小单位。线程与进程类似,不过它们是在同一个进程下执行的,并享有相同的上下文。线程包括开始、执行顺序和结束三部分。它有一个指令指针,用于记录当前运行的上下文。当其他线程运行时,它可以被抢占(中断)和临时挂起(也称为睡眠)——这种做法叫做让步(yielding)
    一个进程中的各个线程与主线程共享同一片数据空间,因此相比于独立的进程而言,线程间的信息共享和通信更加容易。线程一般都是以并发方式执行,正是由于这种并行和数据共享进制,使得多任务间的协作称为了可能

    全局解释器锁

    Python代码的执行是由Python虚拟机进程控制的,对Python虚拟机的访问是由全局解释器锁(GIL)控制的。尽管Python解释器中可以运行多个线程,但是有个这个锁,在任意给定时刻只有一个线程会被解释器执行

    退出线程

    当一个线程完成函数的执行时,它就会退出。不建议使用thread模块的一个明显的原因是:在主线程退出之后,所有其他线程都会在没有清理的情况下直接退出,而另一个模块threading会确保在所有"重要的"子线程退出前,保证整个进程的存活

    thread模块

    thread模块除了派生线程外,还提供了基本的同步数据结构,称为锁对象。其主要的线程方法和锁对象方法有:

    thread 模块的方法 描述
    start_new_thread(function, args, kwargs=None) 派生一个新的线程,使用给定的args和可选的kwargs来执行function
    allocate_lock() 分配LockType锁对象
    exit() 给线程退出指令
    LockType锁对象的方法 描述
    acquire(wait=None) 尝试获取锁对象
    locked() 如果获取了锁对象则返回True,否则返回False
    release() 释放锁

    在下面的例子中,为了实现线程的同步,要对每一个线程都加锁,执行完之后再释放锁,最后一个循环暂停主线程,直到所有锁都被释放后才可以继续执行

    import _thread as thread
    from time import sleep, ctime
    
    loops = [4, 2]   #sleep时间
    
    def loop(nloop, nsec, lock):
        print("start loop", nloop, "at:", ctime())
        sleep(nsec)
        print("loop", nloop, "done at:", ctime())
        lock.release()   #释放锁,每个线程执行完,都会释放自己的锁
    
    def main():
        print("starting at:", ctime())
        locks = []   #创建一个锁列表
        nloops = range(len(loops))   #range(2)
    
        for i in nloops:
            lock = thread.allocate_lock()   #获取锁对象
            lock.acquire()   #获得每个锁,效果相当于"把锁锁上"
            locks.append(lock)  #将锁添加到锁列表中
    
        for i in nloops:
            thread.start_new_thread(loop, (i, loops[i], locks[i]))
    
        for i in nloops:
            while locks[i].locked():
                pass     #等待(暂停主线程),直到所有锁被释放后才会继续执行
    
        print("all DONE at:", ctime())
    
    
    if __name__ == "__main__":
        main()
    

    threading模块

    threading模块提供了更高级别、功能更全面的线程管理。threading模块的Thread类是主要的执行对象,它有thread模块中没有的很多函数

    Thread对象数据属性 描述
    name 线程名
    ident 线程的标识符
    daemon 布尔标志,表示这个线程是否是守护线程

    | Thread对象方法 | 描述 |
    | init(group=None, target=None, name=None, args=(), kwargs={}. verbose=None, daemon=None) | 实例化一个线程对象,需要有一个可调用的target,以及参数args或kwargs,还可以传递name或group参数,不过后者还未实现。此外,verbose标志也是可接受的,而daemon的值会设定thread.daemon属性/标志 |
    | start() | 开始执行该线程 |
    | run() | 定义线程功能的方法(通常在子类中被应用开发者重写) |
    | join(timeout=None) | 直至启动的线程终止之前一直挂起,除非给出了timeout(秒),否则会一直阻塞 |

    使用Thread类,可以有很多方法来创建线程。这里说其中的两种方法:

    • 创建Thread的实例,传给它一个函数
    • 派生Thread的子类,并创建子类的实例

    创建Thread的实例

    在下面的例子中,做了哪些修改呢?使用thread实现的锁没了,取而代之的是一组Thread对象。当实例化每个Thread对象时,把函数(target)和参数(args)传进去,然后得到返回的Thread实例。调用Thread()实例化Thread和调用thread.start_new_thread()的最大区别是新线程不会立即开始执行
    当所有线程都分配完成之后,通过调用每个线程的start()方法让它们开始执行,相比于管理一组锁(分配、获取、释放、检查锁状态等)而言,这里只需要为每个线程调用join()方法即可。join()方法将等待线程结束,或者在提供了超时时间的情况下,达到超时时间。使用join()方法要比等待锁释放的无限循环更加清晰。如果主线程还有其他的事情要做,而不是等待这些线程完成,就可以不调用join()

    import threading
    from time import sleep, ctime
    
    loops = [4, 2]
    
    def loop(nloop, nsec):
        print("start loop", nloop, "at:", ctime())
        sleep(nsec)
        print("loop", nloop, "done at:", ctime())
    
    
    def main():
        print("starting at:", ctime())
        threads = []
        nloops = range(len(loops))
    
        for i in nloops:
            t = threading.Thread(target=loop, args=(i, loops[i]))
            threads.append(t)
    
        for i in nloops:
            threads[i].start()
    
        for i in nloops:
            threads[i].join()
    
        print("all DONE at:", ctime())
    
    
    if __name__ == "__main__":
        main()
    

    派送Thread的子类

    派送出来的子类MyThread继承了父类Thread的构造方法,同时又新增了自己独有的实例属性

    import threading
    from time import sleep, ctime
    
    loops = [4, 2]
    
    class MyThread(threading.Thread):
    
        def __init__(self, func, args, name=""):
            threading.Thread.__init__(self)
            self.name = name
            self.func = func
            self.args = args
    
        def run(self):
            self.func(*self.args)
    
    
    def loop(nloop, nsec):
        print("start loop", nloop, "at:", ctime())
        sleep(nsec)
        print("loop", nloop, "done at:", ctime())
    
    
    def main():
        print("starting at:", ctime())
        threads = []
        nloops = range(len(loops))
    
        for i in nloops:
            t = MyThread(loop, (i, loops[i]), loop.__name__)
            threads.append(t)
            
        for i in nloops:
            threads[i].start()
            
        for i in nloops:
            threads[i].join()
            
        print("all DONE at:", ctime())
        
    
    if __name__ == "__main__":
        main()
    

    还可以对MyThread类进程修改,增加一些调试信息的输出,并将其存储为一个名为myThread的独立模块,以便后面的模块引入调用。除了简单的调用函数外,还可以把结果保存在实例属性self.res,并创建一个新的方法getResult()来获取这个值

    import threading
    from time import ctime
    
    class MyThread(threading.Thread):
    
        def __init__(self, func, args, name=""):
            threading.Thread.__init__(self)
            self.name = name
            self.func = func
            self.args = args
    
    
        def getResult(self):
            return self.res
    
    
        def run(self):
            print("starting", self.name, "at:", ctime())
            self.res = self.func(*self.args)
            print(self.name, "finished at:", ctime())
    
  • 相关阅读:
    Java中替换字符串中特定字符,replaceAll,replace,replaceFirst的区别
    牛客剑指offer 67题(持续更新~)
    从尾到头打印链表
    字符串变形
    缩写
    删除公共字符
    替换空格
    二维数组的查找
    acm博弈论基础总结
    acm模板总结
  • 原文地址:https://www.cnblogs.com/my_captain/p/12830436.html
Copyright © 2011-2022 走看看