zoukankan      html  css  js  c++  java
  • Python学习心得(七) 深入理解threading多线程模块

      Python提供了多个模块来支持多线程编程,包括thread、threading和queue模块等。
    thread模块提供了基本的线程和锁定支持;而threading模块提供了更高级别、功能更全面的线程管理。
    queue模块,用户可以创建一个队列数据结构,用于在多线程之间进行共享。

    核心提示:避免使用thread模块
    推荐使用更高级别的threading模块,原因如下:
    1.threading模块更加先进,有更好的线程支持,并且thread模块中的一些属性会和threading模块有冲突;
    2.低级别的thread模块拥有的同步原语很少(实际上只有一个),而threading模块则有很多;
    3.thread模块对于进程何时退出没有控制,当主线程结束时,所有其他线程也都强制结束,不会发出警告或者进行适当的清理;
    4.threading模块至少能确保重要的子线程在进程退出去前结束。
    5.thread模块不支持守护线程这个概念(thread:当主线程退出时,所有子线程都将终止,不管它们是否仍在工作)

    一.避免使用thread模块,但要了解其基本用法

    import thread 
    from time import sleep,ctime,strftime
    
    loops = [4,2]
    
    def loop(nloop,nsec,lock):
        print 'start loop',nloop,'at:',strftime('%Y-%m-%d %H:%M:%S') 
        sleep(nsec)
        print 'loop',nloop,'done,at:',strftime('%Y-%m-%d %H:%M:%S')
        lock.release() #释放锁
    def main():
        print 'starting at:',strftime('%Y-%m-%d %H:%M:%S')
        locks = []
        nloops = range(len(loops)) #[0, 1]
        for i in nloops:
            lock = thread.allocate_lock() #获取LockType锁对象
            lock.acquire() #取得每个锁(相当于"把锁锁上")
            locks.append(lock) #一旦锁被锁上,就可以把它添加到锁列表locks中了
            #print locks
        for i in nloops: #派生线程
            #每个线程会调用loop()函数,并传递循环号、睡眠时间以及用于该线程的锁
            thread.start_new_thread(loop,(i,loops[i],locks[i]))
            sleep(1)
        for i in nloops:
            while locks[i].locked(): #暂停主线程,等待,直到所有锁都被释放之后才会继续执行
                pass
        print 'all done at:',strftime('%Y-%m-%d %H:%M:%S')
        
    if __name__ == '__main__':
        main()
    

    二.推荐使用更高级别的threading模块,函数式编程法  

    import threading
    #threading模块的Thread类有一个join()方法,可以让主线程等待所有线程执行完毕
    
    from time import sleep,strftime
    
    loops = [4,2,2]
    
    def loop(nloop,nsec):
        print 'start loop',nloop,'at:',strftime('%Y-%m-%d %H:%M:%S') 
        sleep(nsec)
        print 'loop',nloop,'done,at:',strftime('%Y-%m-%d %H:%M:%S')
    def main():
        print 'starting at:',strftime('%Y-%m-%d %H:%M:%S')
        threads = []
        nloops = range(len(loops)) #[0, 1]
        
        for i in nloops:
            #实例化Thread(调用Thread()和之前调用thread.start_new_thread()最大区别在于新线程不会立即开始执行
            t = threading.Thread(target=loop,args=(i,loops[i])) #这是个非常有用的同步功能
            threads.append(t)
    
        for i in nloops: #启动线程
            #当所有线程都分配完成后,通过调用每个线程的start()方法来让它们执行,而不是在这之前就会执行
            threads[i].start()
            
        for i in nloops: 
            #为每个线程调用join()方法,join()方法将等待线程结束,或在提供了超时时间的情况下,达到超时时间。
            threads[i].join() 
            #join()方法只有在你需要等待线程完成的时候才有用
            #join()方法其实根本不需要调用,一旦线程启动,它们就会一直执行,直到给定的函数完成后退出。
            
        print 'all done at:',strftime('%Y-%m-%d %H:%M:%S')
    
    print loop.__name__
        
    if __name__ == '__main__':
        main()
    

    三.threading模块的面向对象编程法,使用可调用的类 

    import threading
    from time import sleep,strftime
    
    loops = [4,2]
    
    class ThreadFunc(object): #添加ThreadFunc类
        def __init__(self,func,args,name = ''): #构造方法设定函数自身,函数参数及函数名的字符串
            self.name = name
            self.func = func
            self.args = args
        def __call__(self): #__call__特殊方法直接调用
            self.func(*self.args)
    
    def loop(nloop,nsec):
        print 'start loop',nloop,'at:',strftime('%Y-%m-%d %H:%M:%S')
        sleep(nsec)
        print 'loop',nloop,'done at:',strftime('%Y-%m-%d %H:%M:%S')
    
    def main():
        print 'starting at:',strftime('%Y-%m-%d %H:%M:%S')
        threads = []
        nloops = range(len(loops))
        
        for i in nloops: #实例化Thread类对象
            t = threading.Thread(target=ThreadFunc(loop,(i,loops[i]),loop.__name__)) #分配线程
            threads.append(t) 
        
        for i in nloops:
            threads[i].start() #真正通过start()方法才启动线程
                               
        
        for i in nloops:
            threads[i].join() #等待线程完成
        print 'all done at:',strftime('%Y-%m-%d %H:%M:%S')   
        
    if __name__ == '__main__':
        main()        
    

    四.threading模块面向对象扩展,派生Thread的子类,并创建子类的实例 

    '''
    import threading
    from time import sleep,strftime
    
    loops = [4,2]
    
    class MyThread(threading.Thread):
        def __init__(self,func,args,name=''):
            threading.Thread.__init__(self) #MyThread子类的构造方法必须先调用基类(即父类)的构造方法
            self.name = name
            self.func = func        
            self.args = args
       
        def run(self): #之前的特殊方法__call__在这里必须要写成run()
            self.func(*self.args)
     
    def loop(nloop,nsec):
        print 'start loop',nloop,'at:',strftime('%Y-%m-%d %H:%M:%S')
        sleep(nsec)
        print 'loop',nloop,'done at:',strftime('%Y-%m-%d %H:%M:%S')
    
    def main():
        print 'starting at:',strftime('%Y-%m-%d %H:%M:%S')
        threads = []
        nloops = range(len(loops)) 
        #print nloops
        
        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:',strftime('%Y-%m-%d %H:%M:%S')
        
    if __name__ == '__main__':
        main()
                       
    '''
    
    #Thread子类MyThread 
    #将上面的代码独立作为一个模块,把结果保存在实例属性self.res中,并创建一个新的方法getResult()来获取这个值
    
    import threading
    from time import strftime
    
    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:',strftime('%Y-%m-%d %H:%M:%S')
            self.res = self.func(*self.args)
            print self.name,'finished at:',strftime('%Y-%m-%d %H:%M:%S')   
    

      

    参考资料《python核心编程》(第3版) 

     

  • 相关阅读:
    数组作为方法参数时的一些意外情况
    pack://application:,,,/
    WPF 使用WinForm Chart控件
    WPF 后台绑定样式
    在转换为 UTC 时大于 DateTime.MaxValue 或小于 DateTime.MinValue 的 DateTime 值无法系列化为 JSON
    LINQ_to_SQL语法及实例大全
    C#编码好习惯,献给所有热爱c#的同学
    C#中OpenFileDialog的使用
    NET 2.0(C#)调用ffmpeg处理视频的方法
    SQLite Mysql 模糊查找(like)
  • 原文地址:https://www.cnblogs.com/binguo2008/p/7352144.html
Copyright © 2011-2022 走看看