Python中的线程详解

时间:2024-09-25 17:05:38

线程

常用的方法

import threading
import time def hello(name):
print('Hello %s' % name)
# 阻塞
time.sleep(5)
print('阻塞了') if __name__ == '__main__': t1 = threading.Thread(target=hello, args=('zhangsan',))
t2 = threading.Thread(target=hello, args=('lisi',))
t1.setName('first') # 设置线程名
start = time.time()
t1.start()
t2.start()
print('---------------')
print(t1.getName())
end = time.time()
print(end-start) # 计算时间

运算的结果如下,可以看到遇到阻塞是直接进行了异步操作,先执行了所有的操作,然后才进行了等待操作,最后结束了程序

Python中的线程详解


import threading
import time def hello(name):
print('你好 %s' % name)
# 阻塞
time.sleep(5)
print('阻塞了') if __name__ == '__main__':
t1 = threading.Thread(target=hello, args=('李',))
t2 = threading.Thread(target=hello, args=('王',))
t1.setName('aaa') # 设置线程名
start = time.time()
t1.start()
t2.start()
t2.join()
print('---------------')
print(t1.getName()) # 获取线程名字
end = time.time()
print(end-start) # 计算时间

运算结果如下:可以看到等待了5秒后才往后执行,join()方法让线程变的毫无意义。

Python中的线程详解

继承类的线程使用方法
import threading
import time class MyThread(threading.Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
print('你好 %s' % self.name)
time.sleep(5)
print('阻塞了')
if __name__ == '__main__': t1 = MyThread('李')
t2 = MyThread('王')
start = time.time()
t1.start()
t2.start()
end = time.time()
print('运行时间 %s 秒' % (end - start)) # 计算时间

计算结果如下所示:

Python中的线程详解

守护进程 setDaemon

不开的状态
import threading
import time def run(n):
print('你好 %s' % n)
time.sleep(2)
def main():
for i in range(5):
t = threading.Thread(target=run, args=(i,))
t.start()
m = threading.Thread(target=main)
m.start()
print('------运行结束-----')

运行结果:

Python中的线程详解

开启后的状态
import threading
import time def run(n):
print('你好 %s' % n)
time.sleep(2)
print('我随后跑')
def main():
for i in range(5):
t = threading.Thread(target=run, args=(i,))
t.start()
m = threading.Thread(target=main)
m.setDaemon(True) # 开启True将主线程设置为守护进程,主线程结束那一刻,其他子线程会同时结束,不论是否执行完毕
m.start()
print('------运行结束-----')

运行结果如下:

可以看到主进程结束,直接程序结束,子进程压根都没运行。这就是守护进程

Python中的线程详解

线程锁Lock

一个进程下可以启动多个线程,多个线程共享父进程的内存空间,每个线程可以访问同一份数据,所以当多个线程同时要修改同一份数据时,就会出现错误。

import threading
import time class MyThread(threading.Thread):
def run(self):
global num
time.sleep(1)
num = num + 1
msg = self.name + 'set num to' + str(num)
print(msg) num = 0
def test():
for i in range(5):
t = MyThread()
t.start() if __name__ == '__main__':
test()

运行结果出现了不可控,线程2没有增加到2,而线程5增加到了2,这是因为没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。这种现象称为“线程不安全”。

Python中的线程详解

加锁
创建锁
lock = threading.Lock()
锁定资源
lock.acquire()
释放资源
lock.release()

import threading
import time class MyThread(threading.Thread):
def run(self):
global num
if lock.acquire():
time.sleep(1)
num = num + 1
msg = self.name + 'set num to' + str(num)
print(msg)
lock.release() num = 0
lock = threading.Lock()
def test():
for i in range(5):
t = MyThread()
t.start() if __name__ == '__main__':
test()

当多个线程都修改某一个共享数据的时候,需要进行同步控制。上锁之后,结果跟我们的预期完全一致。

互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。

Python中的线程详解

递归锁RLock

锁中包含锁


import threading def run1():
lock.acquire() # 小锁
global num
num += 1
lock.release()
return num
def run2():
lock.acquire() # 小锁
global num2
num2 += 1
lock.release()
return num2 def run3():
lock.acquire() # 大锁
res = run1()
res2 = run2()
lock.release()
print(res, res2) if __name__ == '__main__':
num, num2 = 0, 0
lock = threading.RLock() # 生成Rlock
for i in range(10):
t = threading.Thread(target=run3)
t.start() while threading.active_count() != 1: # 线程活动数量不是1,说明子线程还没运行完毕
pass
else:
print('运行结束')

Python中的线程详解

semaphore信号量

同时允许一定数量的线程更改数据

import threading
import time def run(n):
semaphore.acquire()
time.sleep(1)
print('run the thread: %s' % n)
semaphore.release() if __name__ == '__main__':
semaphore = threading.BoundedSemaphore(3) # 设置最多允许3个线程同时运行
for i in range(20):
t = threading.Thread(target=run, args=(i,))
t.start()

3个3个分批次的出现

Python中的线程详解

event事件

实现两个或多个线程间的交互

event.set() 会使event内部为真

event.clear() 会使event内部为假

event.isSet() 判断有没有被设定为真

如果event内部为真,则wait不阻塞,否则会阻塞

import threading

def start():
print('---start---1')
event.wait() # 阻塞
print('---start---2')
if __name__ == '__main__':
event = threading.Event()
t = threading.Thread(target=start)
t.start()
print(event.isSet())

运行结果,程序会阻塞在start1进行不下去

Python中的线程详解

将event内部设置为真

import threading
def start():
print('---start---1')
event.wait() # 阻塞
print('---start---2')
if __name__ == '__main__':
event = threading.Event()
t = threading.Thread(target=start)
t.start()
event.set()
print(event.isSet())

运行结果会从阻塞变为畅通

Python中的线程详解