• Python thread (线程)


    线程 (thread)

    操作系统最小的调度单位,是一串指令的集合
    程序一开始就有一个主线程,新启动的线程和主线程之间互不影响,主线程启动子线程之后就相互独立(子线程也可以启动线程),无论子线程是否执行结束主线程都会继续执行,程序在所有线程执行结束后关闭

    全局解释器锁 (GIL)

    由于无法控制线程执行顺序,为了防止数据出现错误,通过 GIL 使同一时间只有一个线程在工作
    需要明确的一点是 GIL 并不是 Python 的特性,它是在实现 Python 解析器 (CPython) 时所引入的一个概念,Python 完全可以不依赖 GIL

    threading 模块

    启动线程

    直接调用

    示例:

    import threading
    import time
    
    
    def run(i):		# 函数名随意
        print('test', i)
        time.sleep(1)
    
    
    t1 = threading.Thread(target=run, args=('t1',))
    t2 = threading.Thread(target=run, args=('t2',))
    t3 = threading.Thread(target=run, args=('t3',))
    t1.start()
    t2.start()
    t3.start()
    
    

    threading.Thread(target=run, args=('t1',)) 中 target 为线程执行的函数,args 中为传入的参数

    继承式调用

    示例:

    import threading
    
    
    class MyThread(threading.Thread):
        def __init__(self, n):
            super(MyThread, self).__init__()
            self.n = n
    
        def run(self):	# 函数名必须是 run
            print('class test', self.n)
    
    
    t1 = MyThread('t1')
    t2 = MyThread('t2')
    t3 = MyThread('t3')
    t1.start()
    t2.start()
    t3.start()
    
    

    注意:如果需要获得线程执行函数的返回值,可以将返回值放入队列,再从队列中获取 (关于 Python 队列 <- 点击查看)

    多线程与单线程区别

    IO 操作不占用 CPU,计算占用 CPU
    Python 多线程不适合 CPU 密集操作型的任务,适合 IO 操作密集型的任务
    单线程示例:

    import threading
    import time
    
    
    def run(i):
        print('test', i)
        time.sleep(1)
    
    
    run('t1')
    run('t2')
    run('t3')
    
    

    与多线程对比可以发现:多线程是 print 之后等待 1s 之后结束,而单线程每次 print 之后都要等待。

    其他

    join

    主线程创建子线程之后,主线程就与子线程相互独立,不管子线程是否执行完成,主线程都会继续执行下去
    使用 join 可以让主线程等待子线程执行完成之后,再继续执行
    示例:

    import threading
    import time
    
    
    def run(th):
        print('test', th)
        time.sleep(2)
    
    
    start_time = time.time()
    threading_list = []
    num = 0
    for i in range(50):
        t = threading.Thread(target=run, args=('t-%s' % i,))
        t.start()
        threading_list.append(t)
    for item in threading_list:
        item.join()
    print('totally', time.time() - start_time)
    
    

    如果不使用 join 主线程在创建子线程之后就会继续执行,直接输出时间。再等待两秒,所有线程执行结束后程序结束
    使用 join 后主线程会等待相应子线程全部执行结束之后再输出时间

    守护线程(deamon)

    守护线程是为主线程服务的,只要非守护线程执行完成程序就会直接结束
    当一个子线程被设置为守护线程,程序就不会再等待他执行完成再结束
    示例:

    import threading
    import time
    
    
    def run(th):
        print('test', th)
        time.sleep(2)
    
    
    start_time = time.time()
    num = 0
    for i in range(50):
        t = threading.Thread(target=run, args=('t-%s' % i,))
        t.setDaemon(True)
        t.start()
    print('totally', time.time() - start_time)
    
    

    注意setDeamon(true) 需要在 start 之前设置

    线程锁(互斥锁)

    一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据。
    此时,如果多个线程同时修改同一份数据,就会出错
    已经有 GIL 为什么还会出错:
    虽然已经有 GIL 保证同一时刻只有一个线程在修改数据,但是当某个进程在获取数据修改,还没保存修改的结果前 release GIL ,这时就会出错

    import threading
    import time
    
    
    def run(th):
        lock.acquire()	# 获取锁
        global num
        time.sleep(0.01)
        num += 1
        print('test', th)
        lock.release()  # 释放锁
    
    
    lock = threading.Lock()
    start_time = time.time()
    threading_list = []
    num = 0
    for i in range(50):
        t = threading.Thread(target=run, args=('t-%s' % i,))
        t.start()
        threading_list.append(t)
    for item in threading_list:
        item.join()
    print(num)
    
    

    注意:每个线程执行时间不能过长,否则就变成串行了

    死锁

    当有多层互斥锁同时存在时会出现死锁,程序进入死循环

    import threading
    
    
    def run1():
        lock.acquire()
        global num1
        num1 += 1
        lock.release()
        return num1
    
    
    def run2():
        lock.acquire()
        global num2
        num2 += 1
        lock.release()
        return num2
    
    
    def run3():
        lock.acquire()
        res1 = run1()
        res2 = run2()
        lock.release()
        print(res1, res2)
    
    
    num1, num2 = 0, 0
    lock = threading.Lock()
    for i in range(10):
        t = threading.Thread(target=run3)
        t.start()
    
    while threading.active_count() != 1:
        print(threading.active_count())
    else:
        print('-----finished-----')
        print(num1, num2)
    
    

    RLock 递归锁

    为了避免死锁,就需要使用递归锁 RLock

    import threading
    
    
    def run1():
        lock.acquire()
        global num1
        num1 += 1
        lock.release()
        return num1
    
    
    def run2():
        lock.acquire()
        global num2
        num2 += 1
        lock.release()
        return num2
    
    
    def run3():
        lock.acquire()
        res1 = run1()
        res2 = run2()
        lock.release()
        print(res1, res2)
    
    
    num1, num2 = 0, 0
    lock = threading.RLock()
    for i in range(10):
        t = threading.Thread(target=run3)
        t.start()
    
    while threading.active_count() != 1:
        print(threading.active_count())
    else:
        print('-----finished-----')
        print(num1, num2)
    
    

    Semaphore (信号量)

    互斥锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据

    import threading
    import time
    import sys
    
    
    def run(th):
        semaphore.acquire()
        string = 'threading:' + str(th) + '
    '
        sys.stdout.write(string)
        time.sleep(2)
        semaphore.release()
    
    
    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
    else:
        print('Done')
    
    

    从程序的运行过程可以看出:开始有5个线程在运行,这5个线程结束之后又有5个线程启动

    Event (事件)

    Event 可以让线程间进行交互,与设置全局变量同理
    Event 借助internal flag有两种状态:TrueFalse通过setclear改变状态,线程通过is_set()获取 Event 状态,wait()False时会阻塞
    红绿灯与汽车交互示例:

    import threading
    import time
    
    
    event = threading.Event()
    
    
    def light():
        count = 0
        event.set()
        while 1:
            if 5 < count < 10:
                event.clear()
                print('red')
            elif count == 10:
                event.set()
                print('green')
                count = 0
            else:
                print('green')
            count += 1
            time.sleep(1)
    
    
    def car():
        while 1:
            if event.is_set():
                print('running...')
                time.sleep(1)
            else:
                print('waiting')
                event.wait()
    
    
    l1 = threading.Thread(target=light,)
    l1.start()
    c1 = threading.Thread(target=car,)
    c1.start()
    
    
  • 相关阅读:
    Devexpress 控件的BUG, Visual studio 2008(vb.net)编辑器的BUG,或是?
    什么是Dropthings
    希言的日志开张啦
    Application Architecture Guide 2.0 (Chapter 7: Quality Attributes) Part 2
    Application Architecture Guide 2.0 (Chapter 7: Quality Attributes) Part 3
    Application Architecture Guide 2.0 (Chapter 7: Quality Attributes) Part 5
    Application Architecture Guide 2.0 (Chapter 7: Quality Attributes) Part 1
    使用自定义色系美化JfreeChart图表
    Application Architecture Guide 2.0 (Chapter 7: Quality Attributes) Part 4
    [WPF]TextTrimming截断后,ToolTip显示完整信息
  • 原文地址:https://www.cnblogs.com/dbf-/p/11126979.html
Copyright © 2020-2023  润新知