线程同步

来源:互联网 发布:矩阵分解模型 编辑:程序博客网 时间:2024/05/17 00:49

如果多个线程共同对某个数据修改,则可能会出现不可预料的结果。为了保证数据被正确修改,需要对多个线程进行同步。

9.2.1  简单的线程同步

使用Thread对象的Lock和RLock可以实现简单的线程同步。Lock对象和RLock对象都具有acquire方法和release方法。对于如果需要每次只有一个线程操作的数据,可以将操作过程放在acquire方法和release方法之间。如下syn.py脚本使用acquire方法和release方法保持线程同步。

    

# -*- coding:utf-8 -*-
# file: syn.py
#
import threading          # 导入threading模块
import time           # 导入time模块
class mythread(threading.Thread):      # 通过继承创建类
def _init_(self,threadname):      # 初始化方法
threading.Thread._init_(self,name = threadname)  # 调用父类的初始化方法
def run(self):         # 重载run方法
global x          # 使用global表明x为全局变量
lock.acquire()         # 调用lock的acquire方法
for i in range(3):
x = x + 1
time.sleep(2)         # 调用sleep函数,让线程休眠2s
print x
lock.release()         # 调用lock的release方法
lock = threading.RLock()        # 生成RLock对象 
tl = []            # 定义列表
for i in range(10):
t = mythread(str(i))        # 类实例化
tl.append(t)          # 将类对象添加到列表中

x=0             # 将x赋值为0
for i in tl:
i.start()           # 依次运行线程

运行脚本后输出如下所示。

    

3
6
9
12
15
18
21
24
27
30

将syn.py脚本中的第11行的“lock.acquire()”、第16行的“lock.release()”和第17行的“lock = threading.Lock()”删除后保存脚本。运行修改后的脚本输出如下所示。

    

30
30
30
30
30
30
30
30
30
30

修改后的脚本输出都是30也就是i的最终值。由于i是全局变量,在每个线程对i进行操作后就“休眠”了。在线程休眠的时候,Python解释器已经执行了其他的线程,而使i值增加。当所有线程“休眠”结束后,i的值已被所有线程修改变成了30,因此输出为30。

而在使用Lock对象的脚本中,对全局变量i的操作放在acquire方法和release方法之间。Python解释器每次仅允许一个线程对i进行操作。只有当该线程操作完后,并且结束休眠以后才开始下一个线程,所以使用Lock对象的脚本输出是依次递增的。

 

 

9.2.2  使用条件变量保持线程同步

Python的Condition对象提供了对复杂线程同步的支持。使用Condition对象可以在某些事件触发后才处理数据。Condition对象除了具有acquire方法和release方法以外,还有wait方法、notify方法、notifyAll方法等用于条件处理。以下P_C.py脚本使用Condition对象来实现著名的生产者和消费者的关系。

   

# -*- coding:utf-8 -*-
# file: P_C.py
#
import threading          # 导入threading模块
class Producer(threading.Thread):      # 定义生产者类
def _init_(self,threadname):
threading.Thread._init_(self,name = threadname)
def run(self):
global x
con.acquire()         # 调用con的acquire方法
if x == 1000000:
con.wait()         # 调用con的wait方法
pass
else:
for i in range(1000000):
x = x + 1
con.notify()        # 调用con的notify方法
print x
con.release()         # 调用con的release方法
class Consumer(threading.Thread):      # 定义生产者类
def _init_(self,threadname):
threading.Thread._init_(self,name = threadname)
def run(self):
global x
con.acquire()
if x == 0:
con.wait() 
pass
else:
for i in range(1000000):
x = x - 1
con.notify()
print x
con.release()
con = threading.Condition()       # 生成Condition对象
x=0
p = Producer('Producer')        # 生成生产者对象
c = Consumer('Consumer')        # 生成消费者对象
p.start()           # 运行线程
c.start()
p.join()            # 等待线程结束
c.join()
print x

运行脚本后输出如下所示。

    

1000000
0
0

将脚本中第11、13、18、26、28、33、36等行中使用Condition()对象的语句删除。运行修改后的脚本输出如下所示。

    

964166
-482930
-482930

修改后的脚本每次运行后输出可能不一样。

 

9.2.3  使用队列保持线程同步

Python中的Queue对象也提供了对线程同步的支持。使用Queue对象可以实现多生产者和多消费者形成的先进先出的队列。每个生产者将数据依次存入队列,而每个消费者则依次从队列中取出数据。以下MP_MC.py脚本使用Queue对象实现多个生产者和消费者的关系。

    

# -*- coding:utf-8 -*-
# file: MP_MC.py
#
import threading          # 导入threading模块
import time           # 导入time模块
import Queue           # 导入Queue模块
class Producer(threading.Thread):      # 定义生产者类
def _init_(self,threadname):
threading.Thread._init_(self,name = threadname)
def run(self):
global queue         # 声明为全局Queue对象
queue.put(self.getName())      # 调用put方法将线程名添加到
队列中
print self.getName(),'put ',self.getName(),' to queue'
class Consumer(threading.Thread):      # 定义消费者类
def _init_(self,threadname):
threading.Thread._init_(self,name = threadname)
def run(self):
global queue
print self.getName(),'get ',queue.get(),'from queue' # 调用get方法获取队列中内容
queue = Queue.Queue()        # 生成队列对象
plist = []           # 生产者对象列表
clist = []           # 消费者对象列表
for i in range(10):
p = Producer('Producer' + str(i))
plist.append(p)         # 添加到生产者对象列表
for i in range(10):
c = Consumer('Consumer' + str(i))
clist.append(c)         # 添加到消费者对象列表
for i in plist:
i.start()           # 运行生产者线程
i.join()
for i in clist:
i.start()           # 运行消费者线程
i.join()

运行脚本后输出如下所示。

    

Producer0 put  Producer0  to queue
Producer1 put  Producer1  to queue
Producer2 put  Producer2  to queue
Producer3 put  Producer3  to queue
Producer4 put  Producer4  to queue
Producer5 put  Producer5  to queue
Producer6 put  Producer6  to queue
Producer7 put  Producer7  to queue
Producer8 put  Producer8  to queue
Producer9 put  Producer9  to queue
Consumer0 get  Producer0 from queue
Consumer1 get  Producer1 from queue
Consumer2 get  Producer2 from queue
Consumer3 get  Producer3 from queue
Consumer4 get  Producer4 from queue
Consumer5 get  Producer5 from queue
Consumer6 get  Producer6 from queue
Consumer7 get  Producer7 from queue
Consumer8 get  Producer8 from queue
Consumer9 get  Producer9 from queue

 

 

 

原创粉丝点击