[python] 线程锁
参考:http://blog.csdn.net/kobeyan/article/details/44039831
1. 锁的概念
在python中,存在GIL,也就是全局解释器锁,能够保证同一时刻只有一个线程在运行,在这个方面可以认为是线程安全的,但是在线程运行的时候,是共享内存的,共享相同的数据信息,在python的多线程的情况下就不那么安全了。
多线程的主要目的为了提高性能与速度,用在无关的方向是最好的,例如在使用爬虫的时候,可以使用多线程来进行爬取数据,因为在这些线程之间没有需要共同操作的数据,从而在这个时候利用是最好的。
如果需要操作同一份数据,那么必须自己保证数据的安全性。
如果需要利用多cpu的特性,那么应该使用的是多进程编程,而不是多线程编程,多进程编程为multiprocessing。
2. 给线程加锁的原因
第一步:线程A修改了num的值为7
第二步:线程C不知道num的值已经发生了改变,直接调用了num的值7
第三步:线程B对num值加1,此时num值变为8
第四步:线程B使用了num值8
第五步:线程A使用了num值8
因为num只有一个,而三个操作都针对一个num进行,所以上面的操作过程是完全有可能的,而原来线程A、B、C想要使用的num值应该分别为:7、9、8,这里却变成了:8、8、7。试想一下,如果这三个线程的操作对整个程序的执行是至关重要的,会造成什么样的后果?
因此,出于程序稳定运行的考虑,对于线程需要调用内存中的共享数据时,我们就需要为线程加锁。
#!usr/bin/env python
from threading import Thread
from time import sleep, ctime
var =
class IncreThread(Thread):
def run(self):
global var
print 'before,var is ',var
sleep()
var +=
print 'after,var is ',var def use_incre_thread():
threads = []
for i in range():
t = IncreThread()
threads.append(t) for i in range():
threads[i].start()
for t in threads:
t.join()
print 'After 10 times,var is ',var if __name__ == '__main__':
print 'start at:', ctime()
use_incre_thread()
print 'end at:', ctime()
执行结果:
第一次:
start at: Wed Dec ::
before,var is
before,var is
before,var is
before,var is
before,var is
before,var is
before,var is
before,var is
before,var is
before,var is
after,var is
after,var is after,var is after,var is after,var is after,var is after,var is after,var is after,var is after,var is
After times,var is
end at: Wed Dec ::
第二次:
start at: Wed Dec ::
before,var is
before,var is
before,var is
before,var is
before,var is
before,var is
before,var is
before,var is
before,var is
before,var is
after,var is
after,var is
after,var is after,var is after,var is after,var is
after,var is after,var is after,var is after,var is
After times,var is
end at: Wed Dec ::
上述运算过程中,总体消耗时间都是1秒,但是运算结果为7和10,输出也较为混乱。
接下来对线程进行加锁,例子:
#!usr/bin/env python
from threading import Thread, Lock
from time import sleep, ctime
var =
lock = Lock() #创建(设置)锁
class IncreThread(Thread):
def run(self):
global var
lock.acquire() #获取锁
print 'before,var is ',var
sleep()
var +=
print 'after,var is ',var
lock.release() #释放锁 def use_incre_thread():
threads = []
for i in range():
t = IncreThread()
threads.append(t)
for i in range():
threads[i].start()
for t in threads:
t.join()
print 'After 10 times,var is ',var if __name__ == '__main__':
print 'start at:', ctime()
use_incre_thread()
print 'end at:', ctime()
执行结果:
第一次:
start at: Wed Dec ::
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
After times,var is
end at: Wed Dec ::
第二次:
start at: Wed Dec ::
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
before,var is
after,var is
After times,var is
end at: Wed Dec ::
在加锁后,两次执行结果一致(10,大家也可以多尝试几次),但消耗时间为10秒(主要是因为锁,保证了同一时刻只有一个线程在运行,也就是只有一个线程释放锁之后,下一个线程才能执行),总体上按照一下的方式进行执行:
创建(设置)锁Lock();
获取锁;
切换到一个线程去运行;
运行:
指定数量的字节码指令,或者
线程主动让出控制(可以调用times.sleep())
把线程设置成睡眠状态;
解锁;
重复以上步骤。
注:分析一下上面的程序:在某一线程修改var的值时,即给该线程加锁,该线程加锁后,只要是该线程需要调用的代码以及涉及的内存空间,都会立即被锁上,比如这里的"var+=1",其它线程虽然也在并发同时执行,但是不能执行"var+=1"这行代码的,即不能够去访问或修改var这一个共享内存空间的数据,只能等待该线程解锁后才能执行;当该线程解锁后,另一个线程马上加锁再来修改var的值,同时也不允许其它线程占用,如此类推,直到所有线程执行完毕。
另一个加锁实例:
#coding: utf-
import threading
import time counter =
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();
if counter_lock.acquire(): #当需要独占counter资源时,必须先锁定,这个锁可以是任意的一个锁,可以使用上边定义的3个锁中的任意一个
counter +=
print "I am %s, set counter:%s" % (self.name,counter)
counter_lock.release() #使用完counter资源必须要将这个锁打开,让其他线程使用 if __name__ == "__main__":
for i in xrange(,):
my_thread = MyThread(i)
my_thread.start()
再来看两个加锁例子:
example 1
import threading
import time number = 0 lock = threading.RLock() def run(num):
lock.acquire()
global number
number += 1
lock.release()
print number
time.sleep(1) if __name__ == "__main__":
print "start at:",time.ctime()
for i in range(20):
t = threading.Thread(target=run, args=(i,))
t.start()
print "end at:", time.ctime()
输出结果:
start at: Fri Dec 16 16:33:02 2016
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
end at: 20Fri Dec 16 16:33:02 2016
example 2
start at: Fri Dec 16 16:40:07 2016
1
end at: Fri Dec 16 16:40:07 2016 #希望各位学者解释这一步的原因
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/mnt/hgfs/Python/day6$ time python thread_clock6.py | grep 'real' real 0m20.073s
user 0m0.024s
sys 0m0.008s
由执行时间可以更好的说明上面的执行过程,但为什么会这样呢?下面来分析一下:由(2)的分析可知,虽然20个线程都是在同时并发执行run这一个函数,这里与(2)不同在于,(1)只加锁了涉及修改number的程序代码,而这里是加锁了整个函数!所以在20个线程同时开始并发执行这个函数时,由于每一个线程的执行都要加锁,并且加锁的是整个执行的函数,因此其它线程就无法调用该函数中的程序代码,只能等待一个线程执行完毕后再调用该函数的程序代码,如此一来,一个线程的执行需要sleep(1)一次,则20个线程的执行就需要sleep(1) 20次,并且该过程是串行的,因此我们才看到如上面所说的程序执行过程,也可以清晰的知道为什么程序的执行需要20s了。
[python] 线程锁的更多相关文章
- python线程锁
import time,threading balance = 0 lock = threading.Lock() def change_it(n): global balance balance = ...
- Python 第八篇:异常处理、Socket语法、SocketServer实现多并发、进程和线程、线程锁、GIL、Event、信号量、进程间通讯
本节内容: 异常处理.Socket语法.SocketServer实现多并发.进程和线程.线程锁.GIL.Event.信号量.进程间通讯.生产者消费者模型.队列Queue.multiprocess实例 ...
- python 线程/线程锁/信号量
单线程 #常规写法 import threading import time def sayhi(num): # 定义每个线程要运行的函数 print("running on number: ...
- python同步原语--线程锁
多线程锁是python多种同步原语中的其中一种.首先解析一下什么是同步原语,python因为GIL(全局解析锁)的缘故,并没有真正的多线性.另外python的多线程存在一个问题,在多线程编程时,会出现 ...
- python网络编程--线程锁(互斥锁Mutex)
一:为什么需要线程锁 一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况? 很简单,假设你有A,B两 ...
- python线程的GIL问题(全局解释器锁)
造成原因: python ---> 支持线程操作 --->IO的同步和互斥 --> 加锁 ----> 超级锁,给解释器加锁--->解释器同一时刻只能解释一个线程 造成的后 ...
- Python学习---线程锁/信号量/条件变量同步/线程池1221
线程锁 问题现象: 多线程情况下,CPU遇到阻塞会进行线程的切换,所以导致执行了tmp-=1的值还未赋值给num=tmp,另一个线程2又开始了tmp -=1,所以导致最后的值重复赋值给了num,所以出 ...
- Python网络编程(进程通信、信号、线程锁、多线程)
什么是进程通讯的信号? 用过Windows的我们都知道,当我们无法正常结束一个程序时, 可以用任务管理器强制结束这个进程,但这其实是怎么实现的呢? 同样的功能在Linux上是通过生成信号和捕获信号来实 ...
- 操作系统/应用程序、操作中的“并发”、线程和进程,python中线程和进程(GIL锁),python线程编写+锁
并发编程前言: 1.网络应用 1)爬虫 直接应用并发编程: 2)网络框架 django flask tornado 源码-并发编程 3)socketserver 源码-并发编程 2.运维领域 1)自动 ...
随机推荐
- linux 下 用户与用户组
1,增加一个test组:groupadd test 2,将test组重命名test2:groupmod -n test2 test 3,删除test2组:groupdel test2 4,查看当前登录 ...
- SonarQube的安装、配置与使用
SonarQube是管理代码质量一个开放平台,可以快速的定位代码中潜在的或者明显的错误,下面将会介绍一下这个工具的安装.配置以及使用. 准备工作: 1.jdk(不再介绍) 2.sonarqube:ht ...
- 【AT91SAM3S】ADC中断方式采集数据
板子依旧是英倍特的EK-SAM3S.ADC部分的原理图如下: PB1是一个复用引脚,在这里被用作AD功能,对应芯片上的AD5.即,使用片内ADC的5通道测VR1上2号引脚的电压. 实验采用了SysTi ...
- Spring技术揭幕----DispatcherServlet
Spring MVC是一个MVC模式的实现.在Spring MVC的使用中,需要在web.xml中配置DispatcherServlet,也就是说其核心是一个Servlet,这个DispatcherS ...
- 关于iscroll阻止浏览器默认动作
使用iscroll时,移动端遇到需要长按复制功能,但是iscroll屏蔽了浏览器默认事件,所以实现不了. 解决方案: myScroll = new IScroll('#wrapper',{ preve ...
- [Tex学习笔记]积分平均
$\def\avint{\mathop{\mathchoice{\,\rlap{-}\!\!\int} {\rlap{\raise.15em{\scriptstyle -}}\kern-.2em\in ...
- 切服务器时请注意robots.txt文件
最近两天切服务器时又出现测试机器忘记改robots.txt文件的情况,以后就用个二级域名做测试,测试的机器也不对robots.txt作限制.
- (转)AVI文件格式解析+AVI文件解析工具
AVI文件解析工具下载地址:http://download.csdn.net/detail/zjq634359531/7556659 AVI(Audio Video Interleaved的缩写)是一 ...
- 阿里 classloader
http://blog.csdn.net/scythe666/article/details/51956047
- Flex中设置Camera的视频清晰质量的最佳配合
今天需要设置Flex中的Camera组件的一些属性,后来发现清晰度不是很高,于是捣鼓了上午半天,设置了很多的参数,竟然发现手册上就是有现成的一些设置方法,郁闷!不过我还是专门设置了几个有用和必要的属性 ...