多线程

什么是锁?

- 锁通常被用来实现对共享资源的同步访问

- 为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线程需等待其被释放),待资源访问完后,再调用release方法释放锁:

GIL(Global Interpreter Lock) 全局的解释器锁

增加锁的目的:

1.虽然效率十分低,但保证了数据的安全性

2.不同的锁对应保护不同的数据

3.谁拿到GIL锁就让谁得到Cpython解释器的执行权限

4.GIL锁保护的是Cpython解释器数据的安全,而不会保护你自己程序的数据的安全

5.GIL锁当遇到阻塞的时候,就被迫把锁给释放了,那么其他的就开始抢锁了,抢到后把值进行修改,但是第一个拿到锁的还依旧保持着原本的数据,当再次拿到锁的时候,数据已经修改了,而第一位拿的还是原来的数值,这样就造成了混乱,也就保证不了数据的安全了。

同步锁Lock

- GIL与Lock是两把锁,保护的数据不一样,前者是解释器级别的(保护的是解释器级别的数据,比如垃圾回收的数据),后者是保护用户自己开发的应用程序的数据

实例:没有加上锁的情况

- 在执行这个操作的多条bytecodes期间的时候可能中途就换别的线程了,这样就出现了data races的情况

import time
import threading
def addNum():
global num # 在每个线程中都获取这个全局变量
temp = num
print('--get num:',num )
time.sleep(0.01)
num = temp - 1 # 对此公共变量进行-1操作 start = time.time()
num = 100 # 设定一个共享变量
thread_list = []
for i in range(100):
t = threading.Thread(target=addNum)
t.start()
thread_list.append(t) for t in thread_list: # 等待所有线程执行完毕
t.join() print('final num:', num )
end = time.time()
print(end - start)
# 此时并不能获取到正确的答案0
# 100个线程每一个一定都没有执行完就进行了切换,我们说过sleep就等效于IO阻塞,1s之内不会再切换回来,所以最后的结果一定是99.
# 多个线程都在同时操作同一个共享资源,所以造成了资源破坏

实例:加上锁的情况

- 同步锁保证了在同一时刻只有一个线程被执行

import time
import threading def addNum():
global num # 在每个线程中都获取这个全局变量
lock.acquire() # 获取锁
temp = num
print('--get num:',num )
num = temp - 1 # 对此公共变量进行-1操作
lock.release() # 只有在执行完上述内容才会释放锁 start = time.time()
num = 100 # 设定一个共享变量
thread_list = []
lock = threading.Lock() for i in range(100):
t = threading.Thread(target=addNum)
t.start()
thread_list.append(t) for t in thread_list: #等待所有线程执行完毕
t.join() print('final num:', num )
end = time.time()
print(end - start)

死锁Lock

线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁的情况

因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去

import threading,time

class myThread(threading.Thread):
def LoA(self):
lockA.acquire()
print(self.name,"got lockA",time.ctime())
time.sleep(3)
lockB.acquire()
print(self.name,"got lockB",time.ctime())
lockB.release()
lockA.release()
def LoB(self):
lockB.acquire()
print(self.name,"got lockB",time.ctime())
time.sleep(2)
lockA.acquire()
print(self.name,"got lockA",time.ctime())
lockA.release()
lockB.release()
def run(self):
self.LoA()
self.LoB() if __name__=="__main__":
lockA=threading.Lock()
lockB=threading.Lock()
# 解决方案:添加 lock = threading.RLock()
# lock = threading.RLock()
threads=[]
for i in range(3):
threads.append(myThread())
for t in threads:
t.start()
for t in threads:
t.join()
# 此时线程会卡死,一直等待下去,此时添加递归锁即可解决死锁的问题

递归锁 RLock

在添加递归锁后,RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源

信号量

信号量用来控制线程并发数的,它也是一把锁,BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1

计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)

BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常

import threading,time
class myThread(threading.Thread):
def run(self):
if semaphore.acquire(): # 如果上信号量锁就往下进行
print(self.name)
time.sleep(5)
semaphore.release()
if __name__=="__main__":
semaphore = threading.Semaphore(5) # 一次允许5个线程进行
# semaphore = threading.BoundedSemaphore(5) # 与上述效果一致
thrs = []
for i in range(20): # 开启20个线程
thrs.append(myThread())
for t in thrs:
t.start()

条件变量同步

有一类线程需要满足条件之后才能够继续执行,Python提供了threading.Condition对象用于条件变量线程的支持,它除了能提供RLock()或Lock()的方法外,还提供了wait()、notify()、notifyAll()方法

lock_con = threading.Condition([Lock / Rlock]): 默认创建一个RLock锁,用于线程间的通信

wait():条件不满足时调用,线程会释放锁并进入等待阻塞

notify():条件创造后调用,通知等待池激活一个线程

notifyAll():条件创造后调用,通知等待池激活所有线程

import threading,time
from random import randint class Producer(threading.Thread):
def run(self):
global L
while True:
val=randint(0,100)
print('生产者',self.name,":Append"+str(val),L)
if lock_con.acquire():
L.append(val)
lock_con.notify() # 激活一个线程
lock_con.release()
time.sleep(3)
class Consumer(threading.Thread):
def run(self):
global L
while True:
lock_con.acquire() # wait()过后,从此处开始进行
if len(L)==0:
lock_con.wait() # 进入等待阻塞
print('继续进行') # 我们可以看到并没有打印这个话,这说明线程不是从wait()下面继续进行
print('消费者',self.name,":Delete"+str(L[0]),L)
del L[0]
lock_con.release()
time.sleep(0.25) if __name__=="__main__": L = []
lock_con = threading.Condition()
threads = []
for i in range(5):
threads.append(Producer())
threads.append(Consumer())
for t in threads:
t.start()
for t in threads:
t.join()

同步条件(Event)

条件同步和条件变量同步差不多意思,只是少了锁功能,同步条件不是锁

因为条件同步设计于不访问共享资源的条件环境。event = threading.Event():条件环境对象,初始值为False;

event.isSet():返回event的状态值

event.wait():如果 event.isSet()==False将阻塞线程

event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度

event.clear():恢复event的状态值为False

import threading,time
import random
def light():
if not event.isSet():
event.set() #wait就不阻塞 #绿灯状态
count = 0
while True:
if count < 10:
print('\033[42;1m--green light on---\033[0m')
elif count <13:
print('\033[43;1m--yellow light on---\033[0m')
elif count <20:
if event.isSet():
event.clear()
print('\033[41;1m--red light on---\033[0m')
else:
count = 0
event.set() # 打开绿灯
time.sleep(1)
count += 1 def car(n):
while 1:
time.sleep(random.randrange(10))
if event.isSet(): # 绿灯
print("car [%s] is running.." % n)
else:
print("car [%s] is waiting for the red light.." %n) if __name__ == '__main__':
event = threading.Event()
Light = threading.Thread(target=light)
Light.start()
for i in range(3):
t = threading.Thread(target=car,args=(i,))
t.start()

Python学习 :多线程 --- 锁的更多相关文章

  1. Python学习——多线程,异步IO,生成器,协程

    Python的语法是简洁的,也是难理解的. 比如yield关键字: def fun(): for i in range(5): print('test') x = yield i print('goo ...

  2. Python学习---线程锁/信号量/条件变量同步/线程池1221

    线程锁 问题现象: 多线程情况下,CPU遇到阻塞会进行线程的切换,所以导致执行了tmp-=1的值还未赋值给num=tmp,另一个线程2又开始了tmp -=1,所以导致最后的值重复赋值给了num,所以出 ...

  3. Python的多线程锁跟队列

    一.互斥锁: 1.线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁. 2.互斥锁为资源引入一个状态:锁定.非锁定 3.某个线程要更改共享数据是,先将其锁定.此时资源的状态为锁定, ...

  4. day33 python学习 多线程

    线程的概念 进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位. 三 线程与进程的区别 1 1.线程的创建开销小(无需申请内存空间或者资源),创建线程的 ...

  5. python学习-多线程并发

    1.线程与进程 通俗解释: 对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进 ...

  6. Python学习---线程/协程/进程学习 1220【all】

    Python学习---线程基础学习 Python学习---线程锁/信号量/条件变量同步1221 Python学习---同步条件event/队列queue1223 Python学习---进程 1225 ...

  7. python学习笔记- 多线程(1)

    学习多线程首先先要理解线程和进程的关系. 进程 计算机的程序是储存在磁盘中的可执行的二进制文件,执行时把这些二进制文件加载到内存中,操作系统调用并交给处理器执行对应操作,进程是程序的一次执行过程,这是 ...

  8. Python学习day39-并发编程(各种锁)

    figure:last-child { margin-bottom: 0.5rem; } #write ol, #write ul { position: relative; } img { max- ...

  9. python学习笔记-(十三)线程&多线程

    为了方便大家理解下面的知识,可以先看一篇文章:http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html 线程 1.什么是线程? ...

  10. Python多线程锁

    [Python之旅]第六篇(四):Python多线程锁   python lock 多线程 多线程使用方法 多线程锁 摘要:   在多线程程序执行过程中,为什么需要给一些线程加锁以及如何加锁,下面就来 ...

随机推荐

  1. Windows Server 2012 AD域管理创建

    前言 关于AD域管理及其权限划分概论: 1. AD域源于微软,适用于windows,为企业集中化管理和信息安全提供强力保障. 2. 提供域中文件夹共享,但同时又对不同用户有不用的权限. 3.通过对设备 ...

  2. OSG3.0.1的编译

    在OSG-中国有很多关于OSG的资料,包括OSG的编译和教程. 要编译OSG首先需要准备的包: 1,OSG3.0.1源代码: 2,CMAKE: 3,OSG用到的第三方库: 4,OSG Data:OSG ...

  3. Economy a Two-Edged Sword for Democrats

    2017-05-03 12:05:07 https://www.usnews.com/news/blogs/ken-walshs-washington/2014/10/03/economy-a-two ...

  4. fzu_oop_east 第二次作业

    这次有四题: 题目1:(这题本身没难度,就是听说格式比较坑,好像) 代码: #include<iostream> #include<cstdio> using namespac ...

  5. 配置Ceph集群为OpenStack后端存储

    配置Ceph存储为OpenStack的后端存储 1  前期配置 Ceph官网提供的配置Ceph块存储为OpenStack后端存储的文档说明链接地址:http://docs.ceph.com/docs/ ...

  6. 大屏FAQ

    1. 大屏可以分为哪几类?帆软有哪些大屏硬件合作商?编辑 拼接屏:通常由单个46-55寸的液晶显示屏组成屏幕墙,存在拼缝,借助矩阵.屏控系统来进行信号的输入与输出控制,可以实现屏幕墙上多个屏幕的组合. ...

  7. SQL进价2:三值逻辑和null

    1.SQL中的bool类型的值有三种 普通编程语言里的布尔型只有 true 和 false 两个值,这种逻辑体系被称为二值逻辑.而 SQL 语言里,除此之外还有第三个值 unknown,因此这种逻辑体 ...

  8. 【转】CopyOnWriteArrayList

    初识CopyOnWriteArrayList 第一次见到CopyOnWriteArrayList,是在研究JDBC的时候,每一个数据库的Driver都是维护在一个CopyOnWriteArrayLis ...

  9. .NET完全手动搭建三层B/S架构

    简介:三层架构(3-tier application) 通常意义上的三层架构就是将整个业务应用划分为:表现层(WebUI).业务逻辑层(BusinessLogicLayer).数据访问层(DataAc ...

  10. curl http code 0

    使用curl进行post请求后,接收status code ,结果返回的结果是0 ,但是请求返回的数据是正常的. 检查后发现是执行顺序问题: $response = [ 'statusCode' =& ...