文章借用了别人的总结,但是代码demo都是我自己想的。
参考链接:http://www.cnblogs.com/huxi/archive/2010/06/26/1765808.html
http://blog.csdn.net/iamaiearner/article/details/9363837
http://www.tuicool.com/articles/zAJjYj
http://doudouclever.blog.163.com/blog/static/1751123102012111192621448/
http://c4fun.cn/blog/2014/05/06/python-threading/
先从一个简单的demo开始说起:
(pythonenv)[xluren@test thread_communicate]$ cat demo_thread.py
#!/usr/bin/python
import time
def add_sum(n):
global sum,mutex
for i in range(0,n):
# mutex.acquire()
sum+=int(i)
#print threading.currentThread().getName()+"#"+str(sum)+"#"+str(time.time())
# mutex.release()
import threading
threads=[]
global sum,mutex
mutex=threading.Lock()
sum=0
for i in range(100):
threads.append(threading.Thread(target=add_sum,args=(10000,)))
for i in threads:
i.start()
for i in threads:
i.join()
print sum
(pythonenv)[xluren@test thread_communicate]$ python demo_thread.py
4070480886
(pythonenv)[xluren@test thread_communicate]$ python demo_thread.py
3492920367
两次运行的结果不一样。
打开这句的注释你会发现 #print threading.currentThread().getName()+"#"+str(sum)+"#"+str(time.time())
(pythonenv)[xluren@test thread_communicate]$ python demo_thread.py |awk -F'#' '{if(NF>5) print}'
Thread-3#498629#1415598231.61Thread-2#377314#1415598231.6Thread-5#252126#1415598231.61
Thread-4#516899#1415598231.61Thread-6#377314#1415598231.61Thread-1#462700#1415598231.62
Thread-4#752637#1415598231.69Thread-7#589330#1415598231.68Thread-2#1227700#1415598231.7
Thread-2#1331434#1415598231.7Thread-5#1227700#1415598231.7Thread-1#1175952#1415598231.69
原因:也就是说一个线程在计算的过程中用尽了自己的时间片,然后让出了执行的权利,并且此时存在了多个线程同时运行的情况。
也就是说两个线程会同时的去使用同一个数据,而如果是这种情况
线程一:计算了结果,放到内存中,sum在放到内存中前是1000,而线程二,去内存取数据,但是进程一还没有放进去的时候,他取到的是sum=999.在sum=999的基础上线程二去进行计算,必然导致计算的结果是错误的。可以参看UNIX高级环境编程的298页,有详细的介绍,这里涉及到了线程之间的同步和互斥。
线程间的同步和互斥有以下几种情况
通常用来保护多个线程的共享数据的:
代码:
(pythonenv)[xluren@test thread_communicate]$ cat demo_thread_mutex.py
#!/usr/bin/python
import time
def add_sum(n):
global sum,mutex
for i in range(0,n):
mutex.acquire()
sum+=int(i)
print threading.currentThread().getName()+"#"+str(sum)+"#"+str(time.time())
mutex.release()
import threading
threads=[]
global sum,mutex
mutex=threading.Lock()
sum=0
for i in range(100):
threads.append(threading.Thread(target=add_sum,args=(10000,)))
for i in threads:
i.start()
for i in threads:
i.join()
print sum
运行结果:
(pythonenv)[xluren@test thread_communicate]$ python demo_thread_mutex.py |awk -F'#' '{if(NF>5) print}'
(pythonenv)[xluren@test thread_communicate]$ python demo_thread_mutex.py |awk -F'#' '{print NF}'|sort |uniq -c
1 1
1000000 3
Condition(条件变量)通常与一个锁关联。需要在多个Contidion中共享一个锁时,可以传递一个Lock/RLock实例给构造方法,否则它将自己生成一个RLock实例。
可以认为,除了Lock带有的锁定池外,Condition还包含一个等待池,池中的线程处于状态图中的等待阻塞状态,直到另一个线程调用notify()/notifyAll()通知;得到通知后线程进入锁定池等待锁定。
实例方法:
例子是很常见的生产者/消费者模式:
#!/usr/bin/python
# encoding: UTF-8
import threading
import time
def produce():
global product
if con.acquire():
while True:
if product is None:
print 'produce...'
product = 'anything'
con.notify()
con.wait()
time.sleep(2)
def consume():
global product
if con.acquire():
while True:
if product is not None:
print 'consume...'
product = None
con.notify()
con.wait()
time.sleep(2)
product = None
con = threading.Condition()
t1 = threading.Thread(target=produce)
t2 = threading.Thread(target=consume)
t1.start()
t2.start()
Semaphore(信号量)是计算机科学史上最古老的同步指令之一。Semaphore管理一个内置的计数器,每当调用acquire()时-1,调用release() 时+1。计数器不能小于0;当计数器为0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。
实例方法:
[xluren@test thread_communicate]$ cat demo_thread_semaphore.py
#!/usr/bin/python
import time
def add_sum(n):
global sum,sem
for i in range(0,n):
if sem.acquire():
sum+=int(i)
sem.release()
import threading
threads=[]
global sum,sem
sem=threading.Semaphore(1)
sum=0
for i in range(100):
threads.append(threading.Thread(target=add_sum,args=(10000,)))
for i in threads:
i.start()
for i in threads:
i.join()
print sum
测试结果:
[xluren@test thread_communicate]$ python demo_thread_semaphore.py
4999500000
[xluren@test thread_communicate]$
原因分析:因为sem设置的是1,所以同一时刻只有一个线程访问同一变量。这就可以防止内存的数据读写不一致了。
Event(事件)是最简单的线程通信机制之一:一个线程通知事件,其他线程等待事件,Event其实就是一个简化版的 Condition。Event没有锁,无法使线程进入同步阻塞状态,源代码中调用了norifyall
实例代码:
[xluren@test thread_communicate]$ cat demo_event.py
import threading
import random
import time
def deal_event(thread_event):
print threading.currentThread().getName(),"waiting......."
thread_event.wait()
print threading.currentThread().getName(),"done.........."
thread_event = threading.Event()
for i in range(100):
t = threading.Thread(target=deal_event,args=(thread_event,))
t.start()
thread_event.set()
Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。内部实现使用了mutex和condition
示例代码:
[xluren@test thread_communicate]$ cat demo_thread_queue.py
#!/usr/bin/python
import time
def produce_queue(number_queue):
for i in range(100):
print threading.currentThread().getName(),"produce ",i
number_queue.put(i)
time.sleep(1)
def consume_queue(number_queue):
while 1:
n=number_queue.get()
if n>90:
break
else:
print threading.currentThread().getName(),"consume",n
import threading,Queue
threads=[]
number_queue=Queue.Queue()
threads.append(threading.Thread(target=produce_queue,args=(number_queue,)))
threads.append(threading.Thread(target=consume_queue,args=(number_queue,)))
for i in threads:
i.start()
for i in threads:
i.join()
Thread类的构造函数定义如下
Thread类的成员变量和函数如下
互斥锁
threading中定义了两种锁:threading.Lock和threading.RLock。两者的不同在于后者是可重入锁,也就是说在一个线程内重复LOCK同一个锁不会发生死锁,这与POSIX中的PTHREAD_MUTEX_RECURSIVE也就是可递归锁的概念是相同的。
关于互斥锁的API很简单,只有三个函数————分配锁,上锁,解锁。