• 我的Python成长之路---第八天---Python基础(24)---2016年3月5日(晴)


    多线程编程

    什么是多线程,线程是操作系统能够进行运算调度的最小单位。他包含在进程之中,是进程中的实际运作单位。线程是进程中一个单顺序的空值六,一个进程可以并发多个线程,每个线程可以并行处理不同的任务。

    threading模块

    python的标准库提供了两个模块用于多线程处理,_thread和threading,_thread是低级模块,threading是高级模块,是对_thread进行了封装。
    启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行:
    线程有两种调用方式:直接调用和继承式调用

    直接调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #!/usr/bin/env python
    # coding:utf-8
    '''
    Created on: 2016年5月16日
     
    @author: 张晓宇
     
    Email: 61411916@qq.com
     
    Version: 1.0
     
    Description: 多线程演示程序,直接启动方式
     
    Help:
    '''
    def sayhi(num): # 定义每隔线程都要运行的函数
        print('%s is say hi' %num)
        import time
        time.sleep(3)
    import threading
    if __name__ == '__main__':
        t1 = threading.Thread(target = sayhi, args = [1, ]) # 调用Thread方法生成一个线程实例,第一个参数tartget表示进程要执行的函数,args表示要传递给进程函数的参数
        t2 = threading.Thread(target = sayhi, args = [2, ])
        t1.start() # 启动进程
        t2.start()
        t1.join() # 等待子进程完毕,这句话的意思就等待一个进程执行完在执行这句话后面的逻辑,join方法还可以接收一个超时时间参数,表示最多等待多长时间,超过这个时间就不等了,继续执行下面的语句,注意,是不等待,不是中断进程的执行
        t2.join()
        print(t1.getName()) # getName()表示获取进程的名称,默认Thread-1、Thread-2...这种命名方式
        print(t2.getName())

    继承式调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #!/usr/bin/env python
    # coding:utf-8
    '''
    Created on:
     
    @author: 张晓宇
     
    Email: 61411916@qq.com
     
    Version: 1.0
     
    Description: 多线程演示程序,继承式调用
     
    Help:
    '''
    import threading
    class Mythreading(threading.Thread):
        '''
        定义一个类,继承自threading.Thread
        '''
        def __init__(self, num):
            '''
            初始化方法
            :param num:
            :return:
            '''
            threading.Thread.__init__(self)
            self.num = num
     
        def run(self):
            '''
            重写run方法,也就是每个线程要执行的函数
            :return:
            '''
            print('%s is say hi' %self.num)
            import time
            time.sleep(5)
     
    if __name__ == '__main__':
        t1 = Mythreading(1) # 用刚才定义的类创建进程对象
        t2 = Mythreading(2)
        t1.start()
        t2.start()
        t1.join()
        t2.join()
        print(t1.getName())
        print(t2.getName())

    守护线程

    默认情况下,线程执行完毕如果该线程下还有子线程没有执行完毕才会结束,我们可以通过守护线程的方式,是的主线程执行完毕强制结束下面的子线程的运行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    #!/usr/bin/env python3
    # coding:utf-8
    '''
    Created on:
     
    @author: 张晓宇
     
    Email: 61411916@qq.com
     
    Version: 1.0
     
    Description: 守护线程演示程序
     
    Help:
    '''
    import time
    import threading
     
    def child(n):
        '''
        子线程执行的函数
        :param n:
        :return:
        '''
        print('[%s]------running---- ' % n)
        time.sleep(2)
        print('--done--')
     
    def main():
        '''
        主线程要执行的函数
        :return:
        '''
        for i in range(2): # 循环生成5个子线程
            t = threading.Thread(target=child,args=[i,])
            t.start()
            print('starting thread', t.getName())
     
     
    m = threading.Thread(target=main,args=[]) # 创建主线程对象
    m.setDaemon(True) #将主线程设置为Daemon线程,它退出时,其它子线程会同时退出,不管是否执行完任务
    m.start() # 启动主线程
    m.join() # 等待主线程执行完毕
    print("---main thread done----")

    输出结果

    1
    2
    3
    4
    5
    6
    7
    [0]------running----
     
    starting thread Thread-2
    [1]------running----
     
    starting thread Thread-3
    ---main thread done----

    可以看出,main线程下的子线程执行的时间要比main线程时间长,当main线程执行完的时候,子线程的print('--done--')还没有执行就被强制结束了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    #!/usr/bin/env python
    # coding:utf-8
    '''
    Created on:
     
    @author: 张晓宇
     
    Email: 61411916@qq.com
     
    Version: 1.0
     
    Description: 线程锁演示程序
     
    Help:
    '''
    import threading
    import time
    def addNum():
        global num # 调用全局变量num
        print('--get num:', num)
        time.sleep(1)
        #lock.acquire()
        num += 1 # 每个线程都对num进行加1操作
        #lock.release()
        print(num)
     
    if __name__ == '__main__':
        lock = threading.Lock()
        num = 0
        thread_list = [] # 初始化一个线程列表
        for i in range(10000): # 循环启动10000个进程
            t = threading.Thread(target = addNum)
            t.start()
            thread_list.append(t) # 加入到线程列表中
        for t in thread_list: # 循环等待线程列表里的所有线程结束
            t.join()
        print(num) # 打印num的最终值

    正常情况,启动10000个线程,每个线程对num做加1操作,最终的结果将是10000,但是如果你用python2.X版本的解释器执行上面的代码会发现,最结果不总是10000,而且随着线程越多,这个最终结果的值稳定性越差,这就需要在对需要修改线程间共享的变量的时候加一把锁,当一个线程获得锁并操作一个共享变量的时候,其他线程只能等待锁释掉才可以,等待的过程是阻塞的,只有锁被释放掉,其他线程中“抢”到锁的进程才可以继续进行

    申请锁

    1
    lock.acquire()

    释放锁

    1
    lock.release()

    注意,Python3.X已经修复了这个问题,不用加锁结果也是正确的

    递归锁

    递归锁说白了就是一个大锁里面套着小锁,也就是子锁,用到的地方不多,就不说了

    信号量(Semaphore)

    刚才说的锁也是互斥锁,同事只能有一个线程操作,而Semaphore可以同事允许一定数量的线程更改数据。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import threading,time
      
    def run(n):
        semaphore.acquire()
        time.sleep(1)
        print("run the thread: %s " %n)
        semaphore.release()
      
    if __name__ == '__main__':
      
        num= 0
        semaphore  = threading.BoundedSemaphore(5) #最多允许5个线程同时运行
        for i in range(20):
            t = threading.Thread(target=run,args=(i,))
            t.start()
      
    while threading.active_count() != 1:
        pass #print threading.active_count()
    else:
        print('----all threads done---')
        print(num)

    事件Event

    事件可以裂解为就是一个信号,他只有两个状态可以理解为真和假,常用方法如下

    • set():相当于设置为真

    • clear():相当于设置为假

    • isSet():判断是否为真

    • wait():等待事件置为真(阻塞)

    事件做常用的一个地方就是红绿灯模型,下面代码这就是演示红绿灯模型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    #!/usr/bin/env python
    # coding:utf-8
    '''
    Created on: 2016年3月5日
     
    @author: 张晓宇
     
    Email: 61411916@qq.com
     
    Version: 1.0
     
    Description: 红绿灯模型演示程序
     
    Help:
    '''
    import threading
    def light():
        '''
        信号灯进程的线程执行的函数
        :return:
        '''
        import time
        if not event.isSet(): # 判断是否为真,如果不为真就设置为真,也就是一上来是绿灯
            event.set()
        count = 0 # 初始化计数器,可以理解为红绿灯之间的等待时间
        while True: # 无限制循环下去
            if count < 10:
                # 0-10秒是绿灯
                print('33[42;1m--green light on--33[0m')
            elif count < 13:
                # 10-12是黄灯
                print('33[43;1m--yellow light on--33[0m')
            elif count < 20:
                # 14-19秒是红灯
                if event.isSet(): # 判断如果为真,就设置成假的
                    event.clear()
                print('33[41;1m--red light on--33[0m')
            else:
                # 第20秒的时候计数器归零,并从新设置为真
                count = 0
                if not event.isSet():
                    event.set()
            time.sleep(1)
            count += 1 # 计数器加1
     
    def car(n):
        '''
        汽车进程要执行的函数
        :param n:
        :return:
        '''
        import time
        while True:
            time.sleep(1) # 每隔1秒检查一下红绿灯状态
            if event.isSet():
                # 如果为真就runing
                print('car [%s] is running...' %n)
            else:
                # 否则就waiting
                print('car [%s] is waiting for the red light...' %n)
                event.wait() # 等待事件变为真
     
    if __name__ == '__main__':
        event = threading.Event() # 创建Event对象
        Light = threading.Thread(target = light) # 创建信号灯线程
        Light.start() # 启动线程
        for i in range(3): # 循环创建3个汽车线程对象,并启动
            t = threading.Thread(target = car, args = [i,])
            t.start()
  • 相关阅读:
    如何简单实现一个react组件
    css实现弹框
    flex实现流式布局
    classnames的简单使用
    css处理内容溢出
    webpack学习
    github+hexo搭建,运行hexo g报错
    (附代码和截图)spring基于注解的java定时任务功能实现
    关于Cookie、session和localStorage、以及sessionStorage之间的区别和联系,超详细
    今天向大家推荐一个很强的编辑器——notepad++,没有插件plugin Manager的解决办法
  • 原文地址:https://www.cnblogs.com/zhangxiaxuan/p/5498991.html
Copyright © 2020-2023  润新知