zoukankan      html  css  js  c++  java
  • Python的并发并行[1] -> 线程[1] -> 多线程的建立与使用

    多线程的建立与使用


    目录

    1. 生成线程的三种方法
    2. 单线程与多线程对比
    3. 守护线程的设置

    1 生成线程的三种方法

    三种方式分别为:

    1. 创建一个Thread实例,传给它一个函数
    2. 创建一个Thread实例,传给它一个可调用的类实例
    3. 派生Thread的子类,并创建子类的实例
    # There are three ways to create a thread
    # The first is create a thread instance, and pass a function
    # The second one is create a thread instance, and pass the callable instance(obj)
    # The third one is create a subclass of Thread, then generate an instance

    1.1 创建Thread实例并传递一个函数

    在Thread类实例化时,将函数(及其参数)传入,下面的例子中,在实例化Thread类的时候传入了一个函数loop及其参数,生成了一个线程的实例,再启动线程。

     1 # Method one: pass function
     2 from threading import Thread
     3 from time import sleep, ctime
     4 
     5 loops = [4, 2]
     6 
     7 def loop(nloop, nsec):
     8     print('start loop', nloop, 'at', ctime())
     9     sleep(nsec)
    10     print('loop', nloop, 'done at:', ctime())
    11 
    12 def main():
    13     print('starting at:', ctime())
    14     threads = []
    15     nloops = range(len(loops))
    16 
    17     # Create all threads
    18     for i in nloops:
    19         t = Thread(target=loop, args=(i, loops[i]))
    20         threads.append(t)
    21     for i in threads:
    22         i.start()
    23     for i in threads:
    24         i.join()
    25     print('All DONE at:', ctime())
    26     
    27 if __name__ == '__main__':
    28     main()

    1.2 创建Thread实例并传递一个可调用的类实例

    同样,在Thread类实例化时,类(及其参数)传入,下面的例子中,在实例化Thread类的时候传入了一个可调用(__call__函数使类变为可调用)的类实例ThreadFunc,且完成初始化传给target=,此时生成了一个线程的实例,随后启动线程。

     1 # Method two: pass object
     2 
     3 from threading import Thread
     4 from time import sleep, ctime
     5 
     6 loops = [4, 2]
     7 
     8 class ThreadFunc(object):
     9     def __init__(self, func, args, name=''):
    10         self.name = name
    11         self.func = func
    12         self.args = args
    13 
    14     def __call__(self):
    15         self.func(*self.args)
    16 
    17 def loop(nloop, nsec):
    18     print('start loop', nloop, 'at', ctime())
    19     sleep(nsec)
    20     print('loop', nloop, 'done at:', ctime())
    21 
    22 def main():
    23     print('starting at:', ctime())
    24     threads = []
    25     nloops = range(len(loops))
    26 
    27     # Create all threads
    28     for i in nloops:
    29         t = Thread(target=ThreadFunc(loop, (i, loops[i]), loop.__name__))
    30         threads.append(t)
    31     for i in threads:
    32         i.start()
    33     for i in threads:
    34         i.join()
    35     print('All DONE at:', ctime())
    36     
    37 if __name__ == '__main__':
    38     main()

    1.3 派生Thread子类并生成子类实例

    以Thread为基类,派生出一个子类,在子类中重定义run方法,最终生成一个线程实例进行调用。下面的例子中,生成了一个子类MyThread,同时重定义run函数,在初始化时接收一个func参数作为调用函数。

     1 # Method three: by subclass
     2 
     3 from threading import Thread
     4 from time import sleep, ctime
     5 
     6 loops = [4, 2]
     7 
     8 class MyThread(Thread):
     9     def __init__(self, func, args, name=''):
    10         Thread.__init__(self)
    11         self.name = name
    12         self.func = func
    13         self.args = args
    14 
    15     def run(self):
    16         self.func(*self.args)
    17 
    18 def loop(nloop, nsec):
    19     print('start loop', nloop, 'at', ctime())
    20     sleep(nsec)
    21     print('loop', nloop, 'done at:', ctime())
    22 
    23 def main():
    24     print('starting at:', ctime())
    25     threads = []
    26     nloops = range(len(loops))
    27 
    28     # Create all threads
    29     for i in nloops:
    30         t = MyThread(loop, (i, loops[i]), loop.__name__)
    31         threads.append(t)
    32     for i in threads:
    33         i.start()
    34     for i in threads:
    35         i.join()
    36     print('All DONE at:', ctime())
    37     
    38 if __name__ == '__main__':
    39     main()

    2 单线程与多线程对比

    利用单线程与多线程分别进行斐波那契,阶乘与累加操作,此处加入了sleep进行计算延迟,这是由于Python解释器的GIL特性使得Python对于计算密集型的函数并没有优势,而对于I/O密集型的函数则优化性能较好。

     1 from threading import Thread
     2 import time
     3 from time import ctime
     4 
     5 
     6 class MyThread(Thread):
     7     """
     8     Bulid up a Module to make this subclass more general
     9     And get return value by add a function named 'getResult()'
    10     """
    11     def __init__(self, func, args, name=''):
    12         Thread.__init__(self)
    13         self.name = name
    14         self.func = func
    15         self.args = args
    16 
    17     def getResult(self):
    18         return self.res
    19 
    20     def run(self):
    21         print('Starting', self.name, 'at:', ctime())
    22         # Call function here and calculate the running time
    23         self.res = self.func(*self.args)
    24         print(self.name, 'finished at:', ctime())
    25 
    26 def fib(x):
    27     time.sleep(0.005)
    28     if x < 2:
    29         return 1
    30     return (fib(x-2) + fib(x-1))
    31 
    32 def fac(x):
    33     time.sleep(0.1)
    34     if x < 2:
    35         return 1
    36     return (x * fac(x-1))
    37 
    38 def sumx(x):
    39     time.sleep(0.1)
    40     if x < 2:
    41         return 1
    42     return (x + sumx(x-1))
    43 
    44 funcs = [fib, fac, sumx]
    45 n = 12
    46 
    47 def main():
    48     nfuncs = range(len(funcs))
    49     print('***SINGLE THREADS')
    50     for i in nfuncs:
    51         print('Starting', funcs[i].__name__, 'at:', ctime())
    52         print(funcs[i](n))
    53         print(funcs[i].__name__, 'finished at:', ctime())
    54 
    55     print('
    ***MULTIPLE THREADS')
    56     threads = []
    57     for i in nfuncs:
    58         t = MyThread(funcs[i], (n,), funcs[i].__name__)
    59         threads.append(t)
    60     for i in nfuncs:
    61         threads[i].start()
    62     for i in nfuncs:
    63         threads[i].join()
    64         print(threads[i].getResult())
    65     print('All DONE')
    66 
    67 if __name__ == '__main__':
    68     main()

    第 1-24 行,导入所需模块,并派生线程的子类,定义一个返回函数用于返回结果,

    第 26-45 行,分别定义斐波那契,阶乘与累加函数,

    最后在主函数中分别运行两种模式的计算,得到结果

    ***SINGLE THREADS  
    Starting fib at: Tue Aug  1 20:17:47 2017  
    233  
    fib finished at: Tue Aug  1 20:17:50 2017  
    Starting fac at: Tue Aug  1 20:17:50 2017  
    479001600  
    fac finished at: Tue Aug  1 20:17:51 2017  
    Starting sumx at: Tue Aug  1 20:17:51 2017  
    78  
    sumx finished at: Tue Aug  1 20:17:52 2017  
      
    ***MULTIPLE THREADS  
    Starting fib at: Tue Aug  1 20:17:52 2017  
    Starting fac at: Tue Aug  1 20:17:52 2017  
    Starting sumx at: Tue Aug  1 20:17:52 2017  
    sumx finished at: Tue Aug  1 20:17:53 2017  
    fac finished at: Tue Aug  1 20:17:53 2017  
    fib finished at: Tue Aug  1 20:17:54 2017  
    233  
    479001600  
    78  
    All DONE  
    View Code

    从结果中可以看出单线程耗时5秒,而多线程耗时2秒,优化了程序的运行速度。

    Note: 再次注明,Python的多线程并未对计算性能有所提升,此处是由于加入了sleep的等待,因此使得Python的GIL发挥其优势。

    守护线程的设置

    守护线程一般属于后台无限循环的程序,主线程会在所有非守护线程结束之后,自动关闭还在运行的守护线程,而不会等待它的无限循环完成。守护线程的设置只要将线程实例的daemon设置为True即可,默认是False。

     1 import threading
     2 import time
     3 import random
     4 
     5 class MyThread(threading.Thread):
     6     def __init__(self, count):
     7         threading.Thread.__init__(self)
     8         print('%s: There are %d numbers need to be counted' % (self.name, count))
     9         self.count = count
    10 
    11     def run(self):
    12         name = self.name
    13         for i in range(0, self.count):
    14             print('%s counts %d' % (name, i))
    15             time.sleep(1)
    16         print('%s finished' % name)
    17 
    18 
    19 def main():
    20     print('-------Starting-------')
    21     count = random.randint(4, 7)
    22     t_1 = MyThread(count*count)
    23     t_2 = MyThread(count)
    24     t_1.daemon = True
    25     t_2.daemon = False
    26     t_1.start()
    27     t_2.start()
    28     time.sleep(3)
    29     print('---Main Thread End---')
    30 
    31 if __name__ == '__main__':
    32     main()
    33     

    第 5-16 行,派生一个线程子类,run函数实现每秒计数一次的功能

    第 19-29 行,在主函数中,分别生成两个线程实例,其中t_1计数量较大,设为守护线程,t_2计数量较小,设为非守护线程,线程均不挂起,主线程在3秒后会结束。

    运行得到结果

    -------Starting-------  
    Thread-1: There are 36 numbers need to be counted  
    Thread-2: There are 6 numbers need to be counted  
    Thread-1 counts 0  
    Thread-2 counts 0  
    Thread-1 counts 1  
    Thread-2 counts 1  
    Thread-1 counts 2  
    Thread-2 counts 2  
    Thread-1 counts 3  
    ---Main Thread End---  
    Thread-2 counts 3  
    Thread-1 counts 4  
    Thread-2 counts 4  
    Thread-1 counts 5  
    Thread-2 counts 5  
    Thread-1 counts 6  
    Thread-2 finished  
    View Code

    运行主函数后可以看到,两个线程启动后,计数3次时,主线程已经结束,而这时由于t_2是非守护线程,因此主线程挂起等待t_2的计数结束之后,杀掉了还在运行的守护线程t_1,并且退出了主线程。

    相关阅读


    1. 基本概念

    2. threading 模块

    参考链接


    《Python 核心编程 第3版》

  • 相关阅读:
    sqlite3获取所有表信息
    top高级技能
    python xlwt写excel格式控制 颜色、模式、编码、背景色
    python操作Excel的几种方式
    eclispe: 修改所有文件默认编码为UTF-8
    Fragment: 使用newInstance()来实例化fragment(转)
    Java: 线程池(ThreadPoolExecutor)中的参数说明
    android : 解决android无法使用sun.misc.BASE64Encoder sun.misc.BASE64Decoder 的问题, 无需添加rt.jar
    蓝牙BLE: ATT协议层中属性(Attribute)
    蓝牙BLE: ATT和GATT的概念
  • 原文地址:https://www.cnblogs.com/stacklike/p/8158997.html
Copyright © 2011-2022 走看看