python3线程锁方法_python 多线程、线程锁、事件

本文详细介绍了Python中的多线程基础知识,包括线程的创建、线程的同步(如互斥锁、递归锁、信号量、事件)以及守护线程的使用。通过实例展示了线程的join方法、守护线程的设置以及线程锁在避免数据竞争和死锁中的应用,深入理解了线程的同步与通信机制。
摘要由CSDN通过智能技术生成

1. 多线程的基本使用

importthreadingimporttimedefrun(num):print('Num: %s'%num)

time.sleep(3)if num == 4:print('Thread is finished.')

# 对函数 run 创建5个线程for i in range(5):#创建线程,target:目标函数,args:函数的参数,使用元组的形式

t = threading.Thread(target=run,args=(i,))#开始线程活动

t.start()

time.sleep(0.01)print('Main thread is finished.')

结果:

Num: 0

Num:1Num:2Num:3Num:4Main thread is finished.

Thread is finished.

# 上面打印的顺序是先打印Main thread is finished.因为主线程已经完成了,而子线程里 slee(3),所以此时子线程尚未完成,大约3秒后,才打印的 Thread is finished.

PS. 线程类的使用

importthreadingimporttime

num=0

lock= threading.Lock() #互斥锁,因为多个线程要对共享的数据:num 进行修改,防止出现错误。

classMyThread(threading.Thread): # 继承线程类def __init__(self):

super().__init__() #写了__init__,就一定要继承父类的构造方法。父类的__init__的参数是默认参数,故可以不用重写父类的参数。

def task(self): #自定义的函数

globalnumif lock.acquire(): #如果上锁成功,则执行:

num+=1

print(f'{self.name}, Num is : {num}') # 打印线程名和num的值

lock.release()#释放锁,让数据供其他线程使用

time.sleep(1)deftask2(self):

time.sleep(0.1)print(f'{self.name}','Task2 is being called.')

time.sleep(1)def run(self): #重写父类的run方法。每个单独的线程控制中,start()会自动调用 run()方法。

self.task()

self.task2()for i in range(10):

T= MyThread() #针对每个线程对象,start()最多只能调用一次,否则会抛出错误。所以才每循环一次,在循环内部重新生成一个对象

T.start()

2. 等待子线程执行:join

importthreadingimporttimedefrun(num):print('Num: %s'%num)

time.sleep(3)if num == 4:print('Thread is finished.')

Tlist= []for i in range(5):#创建线程,target:目标函数,args:函数的参数,使用元组的形式

t = threading.Thread(target=run,args=(i,))#开始线程活动

t.start()

Tlist.append(t)for t in Tlist: #针对每个子线程,等待子线程执行一段时间,再继续往下执行主线程。

#t.join(0.1) # 针对每个子线程,等待0.1秒,继续执行主线程

t.join() #没写时间,则默认等待子线程执行完毕,才继续往下执行主线程。

time.sleep(0.01)print('Main thread is finished.')

结果:

Num: 0

Num:1Num:2Num:3Num:4Thread is finished.

Main thread is finished.

# 子线程使用了join方法,等待子线程执行完毕才继续执行主线程,所以打印顺序是正常的。

3. 守护线程

即只要主线程执行完毕,不管子线程状态如何,都会被强制杀掉。

importthreadingimporttimedefrun(num):print('Num: %s'%num)

time.sleep(3)if num == 4:print('Thread is finished.')

Tlist=[]for i in range(5):#创建线程,target:目标函数,args:函数的参数,使用元组的形式

t = threading.Thread(target=run,args=(i,))#设置为守护线程

t.setDaemon(True)#开始线程活动

t.start()

Tlist.append(t)for t in Tlist: #针对每个子线程,等待子线程执行一段时间,再继续往下执行主线程。

t.join(0.1) #对子线程等待0.1秒后,继续执行主线程

#t.join() # 没写时间,则默认等待子线程执行完毕,才继续往下执行主线程。

time.sleep(0.01)print('Main thread is finished.')print('Current Thread:',threading.activeCount()) # 打印当前活动线程数量,这是主线程最后一个语句,所以不管此时还有多少个活动的守护线程,当此句执行完毕,守护线程都会被杀掉。

结果:

Num: 0

Num:1Num:2Num:3Num:4Main thread isfinished.

Current Thread:6

# 少打印了Thread is finished. 因为 主线程结束后,子线程就被杀掉了。

4. 线程锁

GIL锁: Global Interpreter Lock,全局解释器锁。为了解决多线程之间数据完整性和状态同步的问题,在任意时刻只有一个线程在解释器中运行。这是CPython中的问题。

线程安全:在多线程中,共享的数据同一时间只能由一个线程执行。

互斥锁:(threading.Lock) 并不关心当前是哪个线程占有了该锁;如果该锁已经被占有了,那么任何其它尝试获取该锁的线程都会被阻塞,包括已经占有该锁的线程也会被阻塞。

递归锁:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源(已经占用该锁的线程,可以多次actuire,并不会阻塞)

死锁:死锁就是当一个线程已经占用了锁并且尚未释放,但是又有新的获取该锁的请求,亦或着两个线程分别占据着对方想要获取的锁,此时线程进入了阻塞的状态,就是死锁。

e.g.

互斥锁:threading.Lock

importthreadingclassMyThread(threading.Thread):def run(self): #这是重写的父类的run方法

globalnumif mutex.acquire(): #判断能否加锁,acquire()返回的是加锁的状态,加锁成功就是True

num = num+1msg= self.name+'set num to'+str(num)print(msg)#mutex.acquire() # 这两句话如果不注释掉,就会进入死锁的状态。因为上一个锁还未释放,再次请求加锁,就会阻塞。

#mutex.release()

mutex.release() #释放锁

num =0

mutex=threading.Lock()deftest():for i in range(5):

t=MyThread()

t.start()if __name__ == '__main__':

test()

普通方法:

importthreadingimporttimedefrun(n):globalnumif mutex.acquire(): #返回获取锁的结果,加锁成功为True,失败false

num = num+1msg='Thread-%s set num to' %n +str(num)print(msg)

mutex.release()

time.sleep(2)

num=0

mutex=threading.Lock()for i in range(5):

t= threading.Thread(target=run,args=(i+1,))

t.start()

递归锁:threading.RLock

importthreadingclassMyThread(threading.Thread):defrun(self):globalnumifmutex.acquire():

num= num+1msg= self.name+'set num to'+str(num)print(msg)

mutex.acquire()#虽然再次acquire,但是并不会阻塞

mutex.release()

mutex.release()

num=0

mutex=threading.RLock()deftest():for i in range(5):

t=MyThread()

t.start()if __name__ == '__main__':

test()

5. 信号量 semaphore

信号量用于限制同时运行的线程数量

importthreadingimporttimedefrun(n):

sema.acquire()print('Thread: %s' %n)

time.sleep(2)

sema.release()

# 声明信号量,同时允许5个线程执行

sema= threading.BoundedSemaphore(5)for i in range(20):

t= threading.Thread(target=run,args=(i+1,))

t.start()

6. 事件:Event

事件可以用于多个线程进行简单通讯,事件可以控制一个内部 flag,初始时是 False,通过 set 方法使其变成 True,clear 方法使其变成 False,flag 为 False 时 wait 方法会一直阻塞直到flag 为True。

importthreadingimporttime#事件可以控制一个内部标志,通过set方法使其变成True,clear方法使其变成False,为False时wait方法会一直阻塞直到标志为True。#这个标志初始时是False

event=threading.Event()#event.set() # 设置flag为True#event.clear() # 设置flag为False#event.wait() # 一直等待(阻塞),直到有线程使用 set 方法将 flag 变成 True#event.is_set() # 返回flag的值

defboss():print('Boss: 今天加班5秒钟')

event.clear() # 清除 flag,使其值变为 false

time.sleep(5)print("Boss: 时间到了,大家回家吧")

event.set() # 设置 flag,使其值变为 Truedefemployee():whileTrue:ifevent.is_set(): # 判断:如果 flag 为 Trueprint('Employee: 回家了!')break

else:print('Emplyee: 工作中,请勿打扰...')

event.wait() # 如果 flag 为 False,则使用 wait 方法一直等待,直到有线程将其设置为 True

# 创建两个线程,分别为boss和employee,让它们根据 event 进行简单的通讯

b= threading.Thread(target=boss,)

e= threading.Thread(target=employee,)

b.start()

e.start()

结果:

Boss: 今天加班5秒钟

Emplyee: 工作中,请勿打扰...

Boss: 时间到了,大家回家吧

Employee: 回家了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值