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)
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)
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.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):
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)
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)
light = threading.Thread(target=lighter, )
light.start()
car = threading.Thread(target=car, args=('MINT',))
car.start()
endTime = time.time()
'''
线程池
由于线程预先被创建并放入线程池中,同时处理完当前任务之后并不销毁而是被安排处理下一个任务,
因此能够避免多次创建线程,从而节省线程创建和销毁的开销,能带来更好的性能和系统稳定性。
'''
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__':
for i in range(3):
t = Thread(target=do_job)
t.daemon = True
t.start()
time.sleep(3)
for i in range(10):
queue.put(i)
queue.join()