参考:http://blog.csdn.net/kobeyan/article/details/44039831

  

1. 锁的概念

  在python中,存在GIL,也就是全局解释器锁,能够保证同一时刻只有一个线程在运行,在这个方面可以认为是线程安全的,但是在线程运行的时候,是共享内存的,共享相同的数据信息,在python的多线程的情况下就不那么安全了。

  多线程的主要目的为了提高性能与速度,用在无关的方向是最好的,例如在使用爬虫的时候,可以使用多线程来进行爬取数据,因为在这些线程之间没有需要共同操作的数据,从而在这个时候利用是最好的。

  如果需要操作同一份数据,那么必须自己保证数据的安全性。

  如果需要利用多cpu的特性,那么应该使用的是多进程编程,而不是多线程编程,多进程编程为multiprocessing。

2. 给线程加锁的原因

    我们知道,不同进程之间的内存空间数据是不能够共享的,试想一下,如果可以随意共享,谈何安全?但是一个进程中的多个线程是可以共享这个进程的内存空间中的数据的,比如多个线程可以同时调用某一内存空间中的某些数据(只是调用,没有做修改)。

    试想一下,在某一进程中,内存空间中存有一个变量对象的值为num=8,假如某一时刻有多个线程需要同时使用这个对象,出于这些线程要实现不同功能的需要,线程A需要将num减1后再使用,线程B需要将num加1后再使用,而线程C则是需要使用num原来的值8。由于这三个线程都是共享存储num值的内存空间的,并且这三个线程是可以同时并发执行的,当三个线程同时对num操作时,因为num只有一个,所以肯定会存在不同的操作顺序,想象一下下面这样操作过程:
第一步:线程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] 线程锁的更多相关文章

  1. python线程锁

    import time,threading balance = 0 lock = threading.Lock() def change_it(n): global balance balance = ...

  2. Python 第八篇:异常处理、Socket语法、SocketServer实现多并发、进程和线程、线程锁、GIL、Event、信号量、进程间通讯

    本节内容: 异常处理.Socket语法.SocketServer实现多并发.进程和线程.线程锁.GIL.Event.信号量.进程间通讯.生产者消费者模型.队列Queue.multiprocess实例 ...

  3. python 线程/线程锁/信号量

    单线程 #常规写法 import threading import time def sayhi(num): # 定义每个线程要运行的函数 print("running on number: ...

  4. python同步原语--线程锁

    多线程锁是python多种同步原语中的其中一种.首先解析一下什么是同步原语,python因为GIL(全局解析锁)的缘故,并没有真正的多线性.另外python的多线程存在一个问题,在多线程编程时,会出现 ...

  5. python网络编程--线程锁(互斥锁Mutex)

    一:为什么需要线程锁 一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况? 很简单,假设你有A,B两 ...

  6. python线程的GIL问题(全局解释器锁)

    造成原因: python ---> 支持线程操作 --->IO的同步和互斥 --> 加锁 ----> 超级锁,给解释器加锁--->解释器同一时刻只能解释一个线程 造成的后 ...

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

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

  8. Python网络编程(进程通信、信号、线程锁、多线程)

    什么是进程通讯的信号? 用过Windows的我们都知道,当我们无法正常结束一个程序时, 可以用任务管理器强制结束这个进程,但这其实是怎么实现的呢? 同样的功能在Linux上是通过生成信号和捕获信号来实 ...

  9. 操作系统/应用程序、操作中的“并发”、线程和进程,python中线程和进程(GIL锁),python线程编写+锁

    并发编程前言: 1.网络应用 1)爬虫 直接应用并发编程: 2)网络框架 django flask tornado 源码-并发编程 3)socketserver 源码-并发编程 2.运维领域 1)自动 ...

随机推荐

  1. android学习笔记52——手势Gesture,增加手势、识别手势

    手势Gesture,增加手势 android除了提供了手势检测之外,还允许应用程序把用户手势(多个持续的触摸事件在屏幕上形成特定的形状)添加到指定文件中,以备以后使用 如果程序需要,当用户下次再次画出 ...

  2. ADF_Data Binding系列1_使用Bean Data Control

    2015-02-16 Created By BaoXinjian

  3. MyBatis无法根据中文条件查询出结果

    情况是这样的 , 以英文做参数可以查询到结果 , 以中文做参数则查询不到结果 在mysql workbench中执行sql , 可以查询到结果. 这是mybatis中没有指定utf-8的缘故导致的. ...

  4. js 字符串截取

    substr方法: text.substr(start[,length]); text:要提取子字符串的字符串或String对象.必选 start:子字符串的起始位置.以0开始索引.必选 length ...

  5. Graph单元

    感谢世外苏子恒同学提供   一.调用单元 例:uses graph;   二.初始化 例:initgraph(var graphdriver,graphmode:integer; const path ...

  6. [CF245H] Queries for Number of Palindromes (容斥原理dp计数)

    题目链接:http://codeforces.com/problemset/problem/245/H 题目大意:给你一个字符串s,对于每次查询,输入为一个数对(i,j),输出s[i..j]之间回文串 ...

  7. ServletContextListener使用详解

    在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期. 当Serv ...

  8. 用webview打开网页时,里面有个div带滚动条的,但是在平板上滚动条失效

    android2.3的不支持滚动条,并且scrollTop也不支持的.(设置overflow未hidden就可以支持). function noBarsOnTouchScreen(arg) { var ...

  9. Linux相关文章

    1.linux 中特殊符号用法详解 2.linux之vim命令 3.linux各文件夹的作用 4.修改linux文件权限命令:chmod 5.CentOS 6.6下安装配置Tomcat环境 6.lin ...

  10. css3 3d效果及动画学习

    css参考手册: http://www.phpstudy.net/css3/ http://www.css88.com/book/css/ 呈现3d效果:-webkit-transform-style ...