python 线程的同步和互斥

文章借用了别人的总结,但是代码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页,有详细的介绍,这里涉及到了线程之间的同步和互斥。

线程间的同步和互斥有以下几种情况

1.mutex 互斥锁,

通常用来保护多个线程的共享数据的:

代码:

(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

2.Condition

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()

3.semaphore信号量

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,所以同一时刻只有一个线程访问同一变量。这就可以防止内存的数据读写不一致了。

4.Event事件

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()

5.Queue队列

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类的构造函数定义如下

 
 
class threading.Thread(group=None, target=None, name=None, args=(), kwargs={})
group: 留作ThreadGroup扩展使用,一般没什么用
target:新线程的任务函数名
name: 线程名,一般也没什么用
args: tuple参数
kwargs:dictionary参数

Thread类的成员变量和函数如下

 
 
start() 启动一个线程
run() 线程执行体,也是一般要重写的内容
join([timeout]) 等待线程结束
name 线程名
ident 线程ID
daemon 是否守护线程
isAlive()、is_alive() 线程是否存活
getName()、setName() Name的get&set方法
isDaemon()、setDaemon() daemon的get&set方法

互斥锁

threading中定义了两种锁:threading.Lock和threading.RLock。两者的不同在于后者是可重入锁,也就是说在一个线程内重复LOCK同一个锁不会发生死锁,这与POSIX中的PTHREAD_MUTEX_RECURSIVE也就是可递归锁的概念是相同的。

关于互斥锁的API很简单,只有三个函数————分配锁,上锁,解锁。

 
 
threading.Lock() 分配一个互斥锁
acquire([blocking=1]) 上锁(阻塞或者非阻塞,非阻塞时相当于try_lock,通过返回False表示已经被其它线程锁住。)
release() 解锁

条件变量

条件变量总是与互斥锁一起使用的,threading中的条件变量默认绑定了一个RLock,也可以在初始化条件变量的时候传进去一个自己定义的锁。可用的函数如下

 
 
threading.Condition([lock]) 分配一个条件变量
acquire(*args) 条件变量上锁
release() 条件变量解锁
wait([timeout]) 等待唤醒,timeout表示超时
notify(n=1) 唤醒最大n个等待的线程
notifyAll()、notify_all() 唤醒所有等待的线程


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值