应用多线程技术可以提高应用程序的响应能力,充分利用计算机的CPU资源,为用户提供更好的应用体验。但也会存在因多个线程竞争使用并修改同一数据而产生数据同步的问题。
举一个例子来说明这个问题。考虑一个编程任务场景,一个协同写作系统允许多个作者共同编写图书,任务要求是每个作者可以添加内容到图书,但不能修改和删除别人的内容,作者对内容编辑完成后,需要将自己编辑的内容添加到图书中。
要完成该编程任务,需要定义一个Book类,用于存储图书内容;定义一个Author线程类,用于编辑内容并将编辑后的内容添加到Book对象。
(1)定义一个Book类,设置属性content,存储图书内容,代码如下:
# 声明图书类 class Book: # 图书类构造方法 def __init__(self, incontent): self.content = incontent def getContent(self): return self.content def setContent(self, incontent): self.content = incontent
(2)定义一个Author线程类,设置属性name、docbook,name存储作者名称,docbook对象为传入的图书对象。在run方法中,让线程中断一段时间,模拟创作过程。代码如下:
import threading import time import book # 声明图书类 class Author(threading.Thread): # 作者类构造方法 def __init__(self, inDocBook,inName,inWaitTime): threading.Thread.__init__(self) self.docbook = inDocBook self.name = inName self.waittime = inWaitTime def getDocBook(self): return self.docbook def setDocBook(self, inDocBook): self.docbook = inDocBook def run(self): # 中断线程 content = self.docbook.getContent() + "\n" + self.name + ":我的创作内容" time.sleep(self.waittime) self.docbook.setContent(content)
(3)建立主线程类,创建5个作者线程,开始模拟图书内容创作。代码如下:
import book
import author
import time
def main():
b = book.Book("")
w1 = author.Author(b,"join",2)
w1.start()
w2 = author.Author(b, "May",2)
w2.start()
w3 = author.Author(b, "Kenny",1)
w3.start()
w4 = author.Author(b, "Joyce",2)
w4.start()
w5 = author.Author(b, "Alex",3)
w5.start()
time.sleep(10)
print("共同创作的图书内容:%s" % (b.getContent()))
if __name__ == '__main__':
main()运行程序,程序输出结果如下图所示:
图 1 示例程序输出结果
观察输出结果,发现程序只输出了Alex创作的内容,这个问题就是多线程数据同步的问题,多个作者线程试图同时更新图书对象内容,导致部分作者创作内容丢失。
问题应该出在Author线程类编辑内容和添加内容的代码上:
def run(self): # 中断线程 content = self.docbook.getContent() + "\n" + self.name + ":我的创作内容" time.sleep(self.waittime) self.docbook.setContent(content)
假设有A、B两个Author线程,A 线程先于B线程执行了self.docbook.getContent语句,该语句执行完成后,A线程获取了当前图书内容,当A线程要执行self.docbook.setContent语句时,A线程的运行被中断了。这时,假设B线程被执行,并且顺利完成了self.docbook.getContent和self.docbook.setContent语句,B线程编辑的内容已经存储到Book对象中。当A线程再次被执行时,A线程的self.docbook.setContent语句将重写Book对象的内容,因为A线程已经在B线程之前获取了Book对象的内容,将会导致B线程写入到Book对象的内容被覆盖掉。
要解决Book对象同步的问题,就需要用到Python提供的线程同步方法,下面一节我们将解决本节案例遇到的问题。
在多线程应用程序中,如果多个线程对共享数据仅是使用,则无需对共享数据做同步保护。当线程要对共享数据进行修改时,必须要对修改的共享数据进行同步保护,否则会出现莫名其妙的问题,程序出现的BUG也不好定位。