0
点赞
收藏
分享

微信扫一扫

python学习笔记之多线程threading/Lock/线程守护/线程阻塞/互斥锁/递归锁/信号量/事件/线程池

爱做梦的老巫婆 2022-04-04 阅读 38
python
#!/usr/bin/python
# -*- coding: utf-8 -*-
# @Version  : 1.0
# @Author   : QQ736592720
# @Datetime : 2022/4/4 16:31
# @Project  : 简答题399___baidu_ocr.py
# @File     : 多线程1.py
import threading
from threading import Lock, Thread
import time

'''
                                      python多线程详解
      一般计算(CPU)密集型任务适合多进程,IO密集型任务适合多线程;
      一个进程可拥有多个并行的(concurrent)线程,当中每一个线程,共享当前进程的资源      
      线程存在于进程中,1个进程至少包含一个线程,进程内存开支大,线程消耗cpu,内存共享,从而极大的提升了程序的运行效率。      
      使用多线程来实现并发比使用多进程的性能高得要多。
      进程之间不能共享内存,但线程之间共享内存非常容易。
      操作系统在创建进程时,需要为该进程重新分配系统资源,但创建线程的代价则小得多。
      因此使用多线程来实现多任务并发执行比使用多进程的效率高
'''

'''
                           GIL  全局解释器
        GIL只在cpython中才有,因为cpython调用的是c语言的原生线程,所以他不能直接操
        作cpu,而只能利用GIL保证同一时间只能有一个线程拿到数据。
'''

'''
        守护线程 setDaemon
	    设置守护线程之后,当主线程结束时,子线程也将立即结束,不再执行   
'''


def run(n):
    print('task', n)
    time.sleep(1)
    print('3s')
    time.sleep(1)
    print('2s')
    time.sleep(1)
    print('1s')


if __name__ == '__main__':
    t = threading.Thread(target=run, args=('t1',))
    t.setDaemon(True)  # 把子线程设置为守护线程,必须在start()之前设置
    t.start()
    print('end')

'''
        线程阻塞 join---写在所有start之后
        主线程等待子线程结束    
'''


def run(n):
    print('task', n)
    time.sleep(2)
    print('5s')
    time.sleep(2)
    print('3s')
    time.sleep(2)
    print('1s')


if __name__ == '__main__':
    t = threading.Thread(target=run, args=('t1',))
    t.setDaemon(True)  # 把子线程设置为守护线程,必须在start()之前设置
    t.start()
    t.join()  # 设置主线程等待子线程结束
    print('end')

'''
        互斥锁 Lock 随机调度的,线程不安全
        场景:Lock牺牲了执行效率而获得数据安全,多个Lock嵌套使用容易引起死锁问题
'''


def work():
    global n
    lock.acquire()
    temp = n
    time.sleep(0.1)
    n = temp - 1
    lock.release()


if __name__ == '__main__':
    lock = Lock()
    n = 100
    l = []
    for i in range(100):
        p = Thread(target=work)
        l.append(p)
        p.start()
    for p in l:
        p.join()

'''
        递归锁:RLcok类的用法和Lock类一模一样,但它支持嵌套,在多个锁没有释放的时候一般会使用RLock类
        场景:上面的Lock多个Lock嵌套使用容易引起死锁问题,递归所解决死锁问题
'''
import time
from threading import Thread, RLock

fork_lock = noodle_lock = RLock()


def eat1(name):
    # 死锁就是noodle_lock抢到的钥匙也能打开下面的fork_lock
    noodle_lock.acquire()
    print('%s 抢到了面条111' % name)
    fork_lock.acquire()
    print('%s 抢到了叉子222' % name)
    print('%s 吃面333' % name)
    fork_lock.release()
    noodle_lock.release()


def eat2(name):
    # 这个相当于跟上面的递归锁是同一级的,所以上面抢到了,这个必须等到上述递归锁的counter为0
    # 也就是所有的资源都release掉了之后,才释放资源
    fork_lock.acquire()
    print('%s 抢到了叉子555' % name)
    time.sleep(1)
    noodle_lock.acquire()
    print('%s 抢到了面条666' % name)
    print('%s 吃面777' % name)
    noodle_lock.release()
    fork_lock.release()


for name in ['哪吒', 'egon', 'yuan']:
    t1 = Thread(target=eat1, args=(name,))
    t2 = Thread(target=eat2, args=(name,))
    t1.start()
    t2.start()

'''
        信号量(BoundedSemaphore类)
        互斥锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据,比如厕所有3个坑,
        那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去
'''


def run(n, semaphore):
    semaphore.acquire()  # 加锁
    time.sleep(3)
    print('run the thread:%s\n' % n)
    semaphore.release()  # 释放


if __name__ == '__main__':
    num = 0
    semaphore = threading.BoundedSemaphore(5)  # 最多允许5个线程同时运行
    for i in range(22):
        t = threading.Thread(target=run, args=('t-%s' % i, semaphore))
        t.start()
    while threading.active_count() != 1:
        pass
    else:
        print('----------all threads done-----------')

'''
        线程事件threading.Event
        python线程的事件用于主线程控制其他线程的执行,事件是一个简单的线程同步对象,其主要提供以下的几个方法:
            clear将flag设置为 False
            set将flag设置为 True
            is_set判断是否设置了flag
            wait会一直监听flag,如果没有检测到flag就一直处于阻塞状态
        事件处理的机制:全局定义了一个Flag,当Flag的值为False,那么event.wait()就会阻塞,当flag值为True,
        那么event.wait()便不再阻塞
'''
event = threading.Event()


def lighter():
    count = 0
    event.set()  # 初始者为绿灯
    while True:
        if 5 < count <= 10:
            event.clear()  # 红灯,清除标志位
            print("\33[41;lmred light is on...\033[0m]")
        elif count > 10:
            event.set()  # 绿灯,设置标志位
            count = 0
        else:
            print('\33[42;lmgreen light is on...\033[0m')

        time.sleep(1)
        count += 1


def car(name):
    while True:
        if event.is_set():  # 判断是否设置了标志位
            print('[%s] running.....' % name)
            time.sleep(1)
        else:
            print('[%s] sees red light,waiting...' % name)
            event.wait()
            print('[%s] green light is on,start going...' % name)


# startTime = time.time()
light = threading.Thread(target=lighter, )
light.start()

car = threading.Thread(target=car, args=('MINT',))
car.start()
endTime = time.time()
# print('用时:',endTime-startTime)

'''
        线程池
        由于线程预先被创建并放入线程池中,同时处理完当前任务之后并不销毁而是被安排处理下一个任务,
        因此能够避免多次创建线程,从而节省线程创建和销毁的开销,能带来更好的性能和系统稳定性。
'''
# 创建队列实例, 用于存储任务
from queue import Queue

queue = Queue()


# 定义需要线程池执行的任务
def do_job():
    while True:
        i = queue.get()
        time.sleep(1)
        print('index %s, curent: %s' % (i, threading.current_thread()))
        queue.task_done()


if __name__ == '__main__':
    # 创建包括3个线程的线程池
    for i in range(3):
        t = Thread(target=do_job)
        t.daemon = True  # 设置线程daemon  主线程退出,daemon线程也会推出,即时正在运行
        t.start()

    # 模拟创建线程池3秒后塞进10个任务到队列
    time.sleep(3)
    for i in range(10):
        queue.put(i)

    queue.join()

举报

相关推荐

0 条评论