python线程锁
㈠ python GIL 和 线程锁是不是同一个东西
今天看到一篇文章,讲述的是几个提升python性能的项目:传送门
在看的过程中,接触到一个名词,一个从学python开始就一直看到,但是从来都是一知半解的名词,心里不开心,必须把它搞明白,对了,这个词就是 GIL。网上搜索了一些资料,粗浅的理解了什么是GIL,自己感觉学习的过程比较好,感觉略有收获,老规矩,为了巩固知识,自己整片文章出来写一写,其实好多文章已经写的很完善了,所以这篇随笔,只做知识巩固,如有雷同,请各位原创作者原谅,小菜鸟一枚,如果哪里写的有问题,还请各位前辈不吝指正。
一句话:解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁。
首先,GIL的全名,Global Interpreter Lock,鉴于英文水平,不做名词翻译,以免误导。大体解释一下,这个锁就是用来为了解决Cpython多线程中线程不安全问题引入的一个全局排它锁,它的作用就是在多线程情况下,保护共享资源,为了不让多个线程同时操作共享资源,导致不可预期的结果而加上的锁,在一个线程操作共享资源时,其他线程请求该资源,只能等待GIL解锁。这个设置在Cpython刚引入多线程概念的时候就有了,然后后续的各种包和组件开发都不可避免的受到了GIL的影响,所以有人会说,python在多线程处理的时候很慢。python GIL实现方式类似于如下伪代码:
if __name__ == '__main__':
GIL锁开始运作
主线程做操作
主线程完成操作
GIL锁释放资源
所以多线程共同操作共享资源的时候,有一个线程竞得了资源,它就被GIL锁保护起来,其他线程只能是在那里等着,但是这个时候,线程的休眠唤醒,全部会消耗CPU资源,所以嘞,就会慢。
看到这个时候,我又发现了一个名词:线程安全。这个名词,也是那种特别熟悉,但就是无法清晰的说出它是啥的概念。查了资料,在这记一下:
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。
我自己想了一下,大约就是这样,比如整个列表,俩个线程同时在列表中append操作,如果没有锁的保护,在机缘巧合之下,俩个线程同时先后申请了空间且没来得及插入数据,然后这时列表中只会有一个空间,那么在插入过程中只能有一个数据写入,会造成不可知后果,有可能报错终止,有可能有一个线程操作没成功,那么这个就是线程不安全了,大白话说,只要线程之间没有共享资源,那么就是线程安全的,有共享资源,为了保证线程安全,需要引进锁的机制。
而后的文章中,有前辈做过实验:
过程证明了因为GIL的存在,导致python在使用多线程的时候反而不如顺序执行快。
此处我又温习了一下python线程:
线程的顺序执行还是多线程并发,取决于join函数的位置。join函数的作用是等待当前线程结束,所以每一个线程创建之后,调用start函数,这是在后面跟上该线程的join函数,那么就是顺序执行,如果多个线程先完成创建和start,最后加上join函数,那么就变成了多线程并发。
这就是今天的学习内容,其实所有知识网上都能找到,更想分享的是一种学习的方法,一种本身很不推荐的学习方法,那就是类似于探索性测试的学习,啥不懂就去看啥,有些时候,我们学习东西确实不能非要究其内在,软件行业的学习本身在非本行人事看来就特别神奇且枯燥,所以最初的学习,我们需要整个图形界面,让我们学到的东西有了成就感,如果上来先去研究机器码,那么没几个人愿意学下去,但是不管怎样,既然走上了软件行业的道路,这种探索性,打破砂锅问到底的学习,在我的感觉里应该是必经之路,也就是所谓的底层研究。以安卓开发举例,如果做安卓开发的,虽然能写出很漂亮的界面,解决所有的bug,如果不了解安卓系统linux层的知识,在我的眼里,从未把这种研发看做大牛。当然我并不觉得不了解linux底层的安卓研发可以解决任何bug
当下的软件行业进入了一个神奇的阶段,我已经听过无数遍的理论,培训机构出来就能赚钱,大学读着没用,在这里不讨论教育体制问题,从个人情感上,我觉得大学教育虽然没有教给学生直接找工作的技能,但是给了所有学生一个能够了解基础知识的园地,换而言之,作为行业的一员,总应该有将行业发展起来的觉悟,行业内整体风气,缺乏静下心来的沉淀。在大谈敏捷,行为驱动,机器学习的同时,自己需要静下心来回头看看,基础已然不牢,再走下去是否有些危险。是不是学习软件技术,就是为了获取互联网行业那虚高的工资,是否已经局限于第三方框架,一旦框架出现问题,只能打给客服而束手无策,是否有过没有做任何尝试就将bug归咎于安卓系统,阿里中间件等等,是不是旧技术还没用明白,为了新技术就可以不再去研究。
还是小菜鸟,在此大谈行业发展难免有些放肆,如有不对的地方,还请各位前辈不吝指正
㈡ python 多线程 改变变量需要加锁么
python的锁可以独立提取出来
1
2
3
4
5
6
7
8
mutex = threading.Lock()
#锁的使用
#创建锁
mutex = threading.Lock()
#锁定
mutex.acquire([timeout])
#释放
mutex.release()
概念
好几个人问我给资源加锁是怎么回事,其实并不是给资源加锁, 而是用锁去锁定资源,你可以定义多个锁, 像下面的代码, 当你需要独占某一资源时,任何一个锁都可以锁这个资源
就好比你用不同的锁都可以把相同的一个门锁住是一个道理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import threading
import time
counter = 0
counter_lock = threading.Lock() #只是定义一个锁,并不是给资源加锁,你可以定义多个锁,像下两行代码,当你需要占用这个资源时,任何一个锁都可以锁这个资源
counter_lock2 = threading.Lock()
counter_lock3 = threading.Lock()
#可以使用上边三个锁的任何一个来锁定资源
class MyThread(threading.Thread):#使用类定义thread,继承threading.Thread
def __init__(self,name):
threading.Thread.__init__(self)
self.name = "Thread-" + str(name)
def run(self): #run函数必须实现
global counter,counter_lock #多线程是共享资源的,使用全局变量
time.sleep(1);
if counter_lock.acquire(): #当需要独占counter资源时,必须先锁定,这个锁可以是任意的一个锁,可以使用上边定义的3个锁中的任意一个
counter += 1
print "I am %s, set counter:%s" % (self.name,counter)
counter_lock.release() #使用完counter资源必须要将这个锁打开,让其他线程使用
if __name__ == "__main__":
for i in xrange(1,101):
my_thread = MyThread(i)
my_thread.start()
线程不安全:
最普通的一个多线程小例子。我一笔带过地讲一讲,我创建了一个继承Thread类的子类MyThread,作为我们的线程启动类。按照规定,重写Thread的run方法,我们的线程启动起来后会自动调用该方法。于是我首先创建了10个线程,并将其加入列表中。再使用一个for循环,开启每个线程。在使用一个for循环,调用join方法等待所有线程结束才退出主线程。
这段代码看似简单,但实际上隐藏着一个很大的问题,只是在这里没有体现出来。你真的以为我创建了10个线程,并按顺序调用了这10个线程,每个线程为n增加了1.实际上,有可能是A线程执行了n++,再C线程执行了n++,再B线程执行n++。
这里涉及到一个“锁”的问题,如果有多个线程同时操作一个对象,如果没有很好地保护该对象,会造成程序结果的不可预期(比如我们在每个线程的run方法中加入一个time.sleep(1),并同时输出线程名称,则我们会发现,输出会乱七八糟。因为可能我们的一个print语句只打印出一半的字符,这个线程就被暂停,执行另一个去了,所以我们看到的结果很乱),这种现象叫做“线程不安全”
线程锁:
于是,Threading模块为我们提供了一个类,Threading.Lock,锁。我们创建一个该类对象,在线程函数执行前,“抢占”该锁,执行完成后,“释放”该锁,则我们确保了每次只有一个线程占有该锁。这时候对一个公共的对象进行操作,则不会发生线程不安全的现象了。
于是,我们把代码更改如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# coding : uft-8
__author__ = 'Phtih0n'
import threading, time
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global n, lock
time.sleep(1)
if lock.acquire():
print n , self.name
n += 1
lock.release()
if "__main__" == __name__:
n = 1
ThreadList = []
lock = threading.Lock()
for i in range(1, 200):
t = MyThread()
ThreadList.append(t)
for t in ThreadList:
t.start()
for t in ThreadList:
t.join()
1
2
3
4
5
6
7
8
9
10
11
1 Thread-2
2 Thread-3
3 Thread-4
4 Thread-6
5 Thread-7
6 Thread-1
7 Thread-8
8 Thread-9
9 Thread-5
Process finished with exit code 0
我们看到,我们先建立了一个threading.Lock类对象lock,在run方法里,我们使用lock.acquire()获得了这个锁。此时,其他的线程就无法再获得该锁了,他们就会阻塞在“if lock.acquire()”这里,直到锁被另一个线程释放:lock.release()。
所以,if语句中的内容就是一块完整的代码,不会再存在执行了一半就暂停去执行别的线程的情况。所以最后结果是整齐的。
就如同在java中,我们使用synchronized关键字修饰一个方法,目的一样,让某段代码被一个线程执行时,不会打断跳到另一个线程中。
这是多线程占用一个公共对象时候的情况。如果多个线程要调用多个现象,而A线程调用A锁占用了A对象,B线程调用了B锁占用了B对象,A线程不能调用B对象,B线程不能调用A对象,于是一直等待。这就造成了线程“死锁”。
Threading模块中,也有一个类,RLock,称之为可重入锁。该锁对象内部维护着一个Lock和一个counter对象。counter对象记录了acquire的次数,使得资源可以被多次require。最后,当所有RLock被release后,其他线程才能获取资源。在同一个线程中,RLock.acquire可以被多次调用,利用该特性,可以解决部分死锁问题。
㈢ skynet 为什么不用python 线程锁
物信息、统计、网页制作、计算等多个领域都体现出了强大的功能。python和其他脚本语言如java、R、Perl 一样,都可以直接在命令行里运行脚本程序。工具/原料
python;CMD命令行;windows操作系统
方法/步骤
1、首先下载安装python,建议安装2.7版本以上,3.0版本以下,由于3.0版本以上不向下兼容,体验较差。
2、打开文本编辑器,推荐editplus,notepad等,将文件保存成 .py格式,editplus和notepad支持识别python语法。
脚本第一行一定要写上 #!usr/bin/python
表示该脚本文件是可执行python脚本
如果python目录不在usr/bin目录下,则替换成当前python执行程序的目录。
3、编写完脚本之后注意调试、可以直接用editplus调试。调试方法可自行网络。脚本写完之后,打开CMD命令行,前提是python 已经被加入到环境变量中,如果没有加入到环境变量,请网络
4、在CMD命令行中,输入 “python” + “空格”,即 ”python “;将已经写好的脚本文件拖拽到当前光标位置,然后敲回车运行即可。
㈣ python 多线程锁应该加哪里
匹配模式
re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同)
M(MULTILINE): 多行模式,改变'^'和'$'的行为
S(DOTALL): 点任意匹配模式,改变'.'的行为!
㈤ python线程有几种锁
普通的一个多线程小例子。我一笔带过地讲一讲,我创建了一个继承Thread类的子类MyThread,作为我们的线程启动类。按照规定,重写Thread的run方法,我们的线程启动起来后会自动调用该方法。于是我首先创建了10个线程,并将其加入列表中。
再使用一个for循环,开启每个线程。在使用一个for循环,调用join方法等待所有线程结束才退出主线程。
㈥ Python 进程,线程,协程,锁机制,你知多少
1.线程和进程:
线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。
2.线程、进程与协程:
线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员
协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保持状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。
协程的适用场景: 当程序中存在大量不需要CPU的操作时(IO),适用于协程;
㈦ Python中的各种锁
大致罗列一下:
一、全局解释器锁(GIL)
1、什么是全局解释器锁
每个CPU在同一时间只能执行一个线程,那么其他的线程就必须等待该线程的全局解释器,使用权消失后才能使用全局解释器,即使多个线程直接不会相互影响在同一个进程下也只有一个线程使用cpu,这样的机制称为全局解释器锁(GIL)。GIL的设计简化了CPython的实现,使的对象模型包括关键的内建类型,如:字典等,都是隐含的,可以并发访问的,锁住全局解释器使得比较容易的实现对多线程的支持,但也损失了多处理器主机的并行计算能力。
2、全局解释器锁的好处
1)、避免了大量的加锁解锁的好处
2)、使数据更加安全,解决多线程间的数据完整性和状态同步
3、全局解释器的缺点
多核处理器退化成单核处理器,只能并发不能并行。
4、GIL的作用:
多线程情况下必须存在资源的竞争,GIL是为了保证在解释器级别的线程唯一使用共享资源(cpu)。
二、同步锁
1、什么是同步锁?
同一时刻的一个进程下的一个线程只能使用一个cpu,要确保这个线程下的程序在一段时间内被cpu执,那么就要用到同步锁。
2、为什么用同步锁?
因为有可能当一个线程在使用cpu时,该线程下的程序可能会遇到io操作,那么cpu就会切到别的线程上去,这样就有可能会影响到该程序结果的完整性。
3、怎么使用同步锁?
只需要在对公共数据的操作前后加上上锁和释放锁的操作即可。
4、同步锁的所用:
为了保证解释器级别下的自己编写的程序唯一使用共享资源产生了同步锁。
三、死锁
1、什么是死锁?
指两个或两个以上的线程或进程在执行程序的过程中,因争夺资源或者程序推进顺序不当而相互等待的一个现象。
2、死锁产生的必要条件?
互斥条件、请求和保持条件、不剥夺条件、环路等待条件
3、处理死锁的基本方法?
预防死锁、避免死锁(银行家算法)、检测死锁(资源分配)、解除死锁:剥夺资源、撤销进程
四、递归锁
在Python中为了支持同一个线程中多次请求同一资源,Python提供了可重入锁。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。递归锁分为可递归锁与非递归锁。
五、乐观锁
假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
六、悲观锁
假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。
python常用的加锁方式:互斥锁、可重入锁、迭代死锁、互相调用死锁、自旋锁。
㈧ python有了GIL,为什么还有线程锁
在python的原始解释器CPython中存在着GIL(Global Interpreter Lock,全局解释器锁),因此在解释执行python代码时,会产生互斥锁来限制线程对共享资源的访问,直到解释器遇到I/O操作或者操作次数达到一定数目时才会释放GIL。
所以,虽然CPython的线程库直接封装了系统的原生线程,但CPython整体作为一个进程,同一时间只会有一个获得GIL的线程在跑,其他线程则处于等待状态。这就造成了即使在多核CPU中,多线程也只是做着分时切换而已。
不过muiltprocessing的出现,已经可以让多进程的python代码编写简化到了类似多线程的程度了。
㈨ python多个线程锁可提高效率吗
首先,Python的多线程本身就是效率极低的,因为有GIL(Global Interpreter Lock:全局解释锁)机制的限制,其作用简单说就是:对于一个解释器,只能有一个线程在执行bytecode。
所以如果为了追求传统意义上多线程的效率,在Python界还是用多进程(multiprocessing)吧……
这里你用了多线程,且用了锁来控制公共资源,首先锁这个东西会导致死锁,不加锁反而没有死锁隐患,但会有同步问题。
另外,如果不同线程操作的是不同的文件,是不存在同步问题的,如果操作同一个文件,我建议采用Queue(队列)来处理。
总的来说,用单线程就好了,因为Python多线程本身就没什么效率,而且单线程也不用考虑同步问题了。非要追求效率的话,就用多进程吧,同样也要考虑进程锁。