threading.local代替普通全局变量,互斥锁,死锁

threading.local

在多线程环境下,一每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,
因为局部变量只有线程自己能看见,不会影响其它线程,而全局变量的修改必需加锁(阻塞–效率低)

但是在使用局部变量的时候,就在函数间调用的时候,传递起来很麻烦。

每个函数一层一层调用这个传递过来的参数,很麻烦。用全局变量,不安全,效率低。
每个线程处理不同的student对象,且不能共享。
请看:

from threading import local
import threading
import time
ret = 0
def work(num):
    global ret
    ret = num
    time.sleep(2)
    print(ret)

for i in range(10):
    t = threading.Thread(target=work,args=(i,))
    t.start()

而如果这样使用的话就会造成数据的不安全

所以为了解决这个问题,就引入了threading,local代替全局变量

from threading import local
import threading
import time
# ret = 0
ret =local()
def work(num):
    # global ret
    ret = num  # 给每一个线程开辟一个独立空间来存储值
    time.sleep(2)
    print(ret)

for i in range(10):
    t = threading.Thread(target=work,args=(i,))
    t.start()

在这里插入图片描述
小结:
一个threading.local变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。
threading.local解决了参数在一个线程中各个函数之间互相传递的问题。

同步异步概念

同步异步概念
同步的意思就是协同步调,按预定的先后次序执行。例如你先说完然后我再说。
大家不要将同步理解成一起动作,同步是指协同、协助、互相配合。
例如线程同步,可以理解为线程A和B一块配合工作,A执行到一定程度时要依靠B的某个结果,于是停下来示意B执行,B执行完将结果给A,然后A继续执行。
A强依赖B(对方),A必须等到B的回复,才能做出下一步响应。即A的操作(行程)是顺序执行的,中间少了哪一步都不可以,或者说中间哪一步出错都不可以。
举个例子:
你去外地上学(人生地不熟),突然生活费不够了;此时你决定打电话回家,通知家里转生活费过来,可是当你拨出电话时,对方一直处于待接听状态(即:打不通,联系不上),为了拿到生活费,你就不停的oncall、等待,终可能不能及时要到生活费,导致你今天要做的事都没有完成,而白白花掉了时间。
异步:
异步则相反,A并不强依赖B,A对B响应的时间也不敏感,无论B返回还是不返回,A都能继续运行;B响应并返回了,A就继续做之前的事情,B没有响应,A就做其他的事情。也就是说A不存在等待对方的概念。
举个例子:
在你打完电话发现没人接听时,猜想:对方可能在忙,暂时无法接听电话,所以你发了一条短信(或者语音留言,亦或是其他的方式)通知对方后便忙其他要紧的事了;这时你就不需要持续不断的拨打电话,还可以做其他事情;待一定时间后,对方看到你的留言便回复响应你,当然对方可能转钱也可能不转钱。但是整个一天下来,你还做了很多事情。 或者说你找室友临时借了一笔钱,又开始happy的上学时光了。
对于多线程共享全局变量计算错误的问题,我们可以使用线程同步来进行解决。

互斥锁

当多个线程几乎同时修改一个共享数据的时候,需要进行同步控制,线程同步能够保证多个
线程安全的访问竞争资源(全局内容),简单的同步机制就是使用互斥锁。
某个线程要更改共享数据时,先将其锁定,此时资源的状态为锁定状态,其他线程就能更
改,直到该线程将资源状态改为非锁定状态,也就是释放资源,其他的线程才能再次锁定资
源。互斥锁保证了每一次只有一个线程进入写入操作。从而保证了多线程下数据的安全性。
就好比上厕所,如果进去了一个人他就要给门上锁,然后使用厕所,使用完毕之后,对门进行开锁,然后让后面的人继续使用!
在这里插入图片描述
互斥锁详解图
在这里插入图片描述

死锁

在多个线程共享资源的时候,如果两个线程分别占有一部分资源,并且同时等待对方的资
源,就会造成死锁现象。
如果锁之间相互嵌套,就有可能出现死锁。因此尽量不要出现锁之间的嵌套。
就是说你们两个都不释放资源,就好比,你和你弟弟在吃饭时,你拿着一碗菜但是你没有筷子,而你弟弟有筷子但是没有菜,你们两个谁都不让着谁,这就造成了死锁!

# 导入锁模块
from threading import Lock
# 导入创建线程模块
from threading import Thread
import time
# 死锁
# 设置两把互斥锁锁
lock1 = Lock()
lock2 = Lock()

def work1(num):
    lock1.acquire() # lock1上锁
    time.sleep(1)  # lock1上锁后,延时1秒,等待另外那个线程 把lock2上锁
    print('in work1')
    # 此时会堵塞,因为这个lock2已经被另外的线程抢先上锁了
    lock2.acquire() # lock2上锁
    print('work1---',num)
    lock2.release() # lock2解锁
    lock1.release() # lock1解锁

def work2(num):
    lock2.acquire() # lock2加锁
    time.sleep(1)# lock2上锁后,延时1秒,等待另外那个线程 把lock1上锁
    print('in work2')

    # 此时会堵塞,因为这个lock1已经被另外的线程抢先上锁了
    lock1.acquire() # lock1加锁
    print('work2----',num)
    lock1.release() # lock1解锁
    lock2.release() # lock2解锁

if __name__ == '__main__':
    # 创建线程
    t1 = Thread(target=work1,args=(1000000,))
    t2 = Thread(target=work2,args=(1000000,))
    # 启动线程
    t1.start()
    t2.start()

这时已经被锁住
在这里插入图片描述

线程队列Queue

队列是一种先进先出(FIFO)的存储数据结构,就比如排队上厕所一个道理。
1.创建一个“队列”对象
import Queue # 导入模块
q = Queue.Queue(maxsize = 10)
Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造
函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。
2.将一个值放入队列中 q.put(10)
调用队列对象的put()方法在队尾插入一个项目。 3. 将一个值从队列中取出q.get() 从队头删除并返回一个项目。如果取不到数据则一直等待。
4.q.qsize() 返回队列的大小
5.q.empty() 如果队列为空,返回True,反之False
6.q.full() 如果队列满了,返回True,反之False
7.q.put_nowait(item) ,如果取不到不等待,之间抛出异常。
8.q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一
个信号
9.q.join() 收到q.task_done()信号后再往下执行,否则一直等待。或者开始时没有放数
据join()不会阻塞。
q.task_done() 和 q.join() 通常一起使用。

# 导入线程队列模块
import queue

# 1、创建可以存放3个项目的队列
q = queue.Queue(3)
q.put('zsj')
q.put(123)
q.put([1,2,3])
# 2、因为队列设置的是只可以添加3个项目而这里又添加一个项目,则会造成程序阻塞
# q.put('zsj')  # 阻塞
# 将队列中的数据取出来
print(q.get())
print(q.get())
print(q.get())

# 因为队列设置的是只可以取3个项目而这里要取4个项目,则会造成程序阻塞
# print(q.get())

# 3、因为队列设置的是只可以取3个项目而这里要取第4个项目,则q.get_nowait方法则会造成,如果q中没有数据就直接抛出异常不等待
# print(q.get_nowait())  # 如果q中没有数据就直接抛出异常不等待

# 4、
print(q.empty())  #如果队列为空,返回True,反之False

# 5、
print(q.full())  #如果队列满了,返回True,反之False

qut方法

在这里插入图片描述

get方法

在这里插入图片描述

q.empty()方法和q.full()方法

在这里插入图片描述

q.task_done和q.join方法的使用,两种情况

因为q.task_done和q.join的联合使用只能对一次的qut和get有效所以这里不会进行阻塞!

在这里插入图片描述
会阻塞
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值