多线程
Posted gredae
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。
线程
什么是线程
程序在运行代码的过程。
进程与线程
进程:一个在运行中的程序(具体内容是由线程来运行)的状态,是系统的一个资源单位。
线程:运行指定代码的过程。一个进程中至少有一个线程,但是一个进程可以有多个线程。线程是cpu调度的最小单位。
类比到现实中就是生产车间(指代进程)与流水线(指代线程)的关系。我们可以说这个车间是生产汽车,生产车间里面的每一条流水线都是生产汽车部件的。生产车间是由一个一个流水线组合成地。进程也是由线程构成地。
线程与进程的区别
- 进程之间是内存隔离,线程之间是共享内存
- 创建进程由于需要开辟内存空间,而线程不需要开辟内存空间,所以创建线程的速度比创建进程的速度快
线程创建的两种方式
使用threading模块创建Thread,而且创建线程的方式与multiprocessing创建线程的方式一样(据说是multiprocessing照着threading写的,不要说是我说的)
方式一:实现Thread类
from threading import Thread,currentThread def pr(): print(f'我是进程currentThread().name!') t = Thread(target=pr) t.start() print('我是主进程!') ####################### # 结果: 我是进程Thread-1! # 因为创建线程的速度很快所以创建了线程就执行了代码 我是主进程!
方式二:继承Thread类
from threading import Thread,currentThread class MyT(Thread): def run(self) -> None: # 必须重写run方法 print(f'我是进程currentThread().name') t = MyT() t.start() print('我是主进程!') ####################### # 结果: 我是进程Thread-1! 我是主进程!
线程的属性和方法
join
主线程等待子线程运行结束
from threading import Thread import time def task(): print('子线程开启!') time.sleep(2) print('子线程结束!') t = Thread(target=task) t.start() print('主线程结束!') ###################### # 结果: 子线程开启! 主线程结束! 子线程结束!
isAlive()
线程是否还在运行
from threading import Thread import time def task(): print('子线程开启!') time.sleep(2) print('子线程结束!') t = Thread(target=task) t.start() time.sleep(1) print(t.isAlive()) # True print('主线程结束')
getName()
获取线程名字
from threading import Thread import time def task(): print('子线程开启!') time.sleep(2) print('子线程结束!') t = Thread(target=task) t.start() print(t.getName()) # Thread-1 print('主线程结束')
setName()
设置线程名字
from threading import Thread import time def task(): print('子线程开启!') time.sleep(2) print('子线程结束!') t = Thread(target=task) t.start() print(t.getName()) # Thread-1 t.setName('张三') print(t.getName()) # 张三 print('主线程结束')
currentThread()
获取当前线程对象
from threading import Thread,currentThread import time def task(): print(currentThread().getName()) # Thread-1 currentThread().setName('张三') print(currentThread().getName()) # 张三 t = Thread(target=task) t.start() time.sleep(0.5) print(t.getName()) # 张三 print('主线程结束')
enumerate()
返回当前活动进程
from threading import Thread,enumerate import time def task(): print('子线程开启!') time.sleep(2) print('子线程结束!') t = Thread(target=task) t.start() print(enumerate()) # [<_MainThread(MainThread, started 3068)>, <Thread(Thread-1, started 16348)>] print('主线程结束')
activeCount()
返回当前活动线程数量
from threading import Thread,activeCount import time def task(): print('子线程开启!') time.sleep(2) print('子线程结束!') t = Thread(target=task) t.start() print(activeCount()) # 2 print('主线程结束')
守护线程
守护线程守护至当前进程结束
from threading import Thread
import time
def task():
print('子线程开启!')
time.sleep(2)
print('子线程结束!')
t = Thread(target=task)
t.daemon = True
t.start()
print('主线程结束')
但是,主进程会等待还没有结束的子线程结束后结束
from threading import Thread
import time
def task():
print('守护线程开启!')
time.sleep(2)
print('守护线程结束!')
def pr():
print('子线程开启!')
time.sleep(3)
print('子线程结束!')
t = Thread(target=task)
t.daemon = True
t.start()
t = Thread(target=pr)
t.start()
print('主线程结束')
线程锁
当我们要对线程间共享的资源进行操作,为了资源的安全考虑,就需要为资源加上线程锁。这样就可以保证同一个资源同一时刻只有一个线程有权限去修改。
from threading import Lock,Thread
import time
#初始化一把锁,由于线程间共享内存所以可以不用传递参数
lock=Lock()
count = 1
def buy():
#获取钥匙在acquire()和release()之间的代码在同一时间只允许一个进程执行,当一个进程完成后将钥匙归还后,其他的进程才可以获得钥匙。
lock.acquire() # 拿锁
# 模拟网络的延迟
global count
time.sleep(0.1)
if count>0:
print('买到票了')
count=count-1
with open('access.txt', 'w', encoding='utf-8') as f:
f.write(str(count))
else:
print('没票了')
lock.release() #还锁
count = 1
for i in range(5):
p=Thread(target=buy)
p.start()
死锁
当程序中有两把锁,并且一个线程需要拿到两把锁才能运行下去的时候,就会发生死锁的问题
from threading import Lock,Thread,currentThread
import time
lock1 = Lock()
lock2 = Lock()
def func1():
lock2.acquire()
print('\033[41m%s 拿到A锁\033[0m' % currentThread().name)
lock1.acquire()
print('\033[42m%s 拿到B锁\033[0m' % currentThread().name)
lock1.release()
lock2.release()
def func2():
lock1.acquire()
print('\033[43m%s 拿到B锁\033[0m' % currentThread().name)
time.sleep(2)
lock2.acquire()
print('\033[44m%s 拿到A锁\033[0m' % currentThread().name)
lock2.release()
lock1.release()
t1 = Thread(target=func2)
t2 = Thread(target=func1)
t1.start()
t2.start()
解决死锁问题可以使用 递归锁
递归锁
在一个线程中是的锁可以被多次获取,但是不同线程间只能有一个线程获得锁
from threading import RLock,Thread,currentThread
import time
lock1 = RLock()
lock2 = lock1
def func1():
lock2.acquire()
print('\033[41m%s 拿到A锁\033[0m' % currentThread().name)
lock1.acquire()
print('\033[42m%s 拿到B锁\033[0m' % currentThread().name)
lock1.release()
lock2.release()
def func2():
lock1.acquire()
print('\033[43m%s 拿到B锁\033[0m' % currentThread().name)
time.sleep(2)
lock2.acquire()
print('\033[44m%s 拿到A锁\033[0m' % currentThread().name)
lock2.release()
lock1.release()
t1 = Thread(target=func2)
t2 = Thread(target=func1)
t1.start()
t2.start()
GIL(全局解释器锁)
因为cpython自带的垃圾回收机制不是线程安全的,所以要有GIL锁来保证同一时间只有一个线程在解释器中运行。但是这会导致cpython中的多线程并不是真正意义上的多线程,它只能运用到单核的性能。
解决这个问题主要有两种办法:
- 使用多线程
- 使用C语言写多线程的部分,再使用ctypes模块
具体可以看:https://blog.csdn.net/IAlexanderI/article/details/72932309
线程间通信
队列
先进先出
import queue q = queue.Queue() q.put('123') q.put('qweqwe') print(q.get()) print(q.get()) ############### # 结果: 123 qweqwe
栈
后进先出
import queue q = queue.LifoQueue() q.put('123') q.put('qwe') q.put('asd') print(q.get()) print(q.get()) print(q.get()) ################ # 结果: asd qwe 123
优先级
按照元组中的优先级输出
import queue q = queue.PriorityQueue() q.put((4,'123')) q.put((2,'qwe')) q.put((3,'asd')) print(q.get()) print(q.get()) print(q.get()) ################ # 结果: (2, 'qwe') (3, 'asd') (4, '123')
线程定时器
倒计时,当时间归零开始执行线程
from threading import Timer
def task():
print('线程开始执行')
t = Timer(4,task) # 过了4s后开启了一个线程
t.start()
以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章