ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# Python多线程 多线程类似于同时执行多个不同程序,多线程运行有如下优点: * 使用线程可以把占据长时间的程序中的任务放到后台去处理。 * 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度 * 程序的运行速度可能加快 * 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。 指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。 * 线程可以被抢占(中断)。 * 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。 ## 开始学习Python线程 Python中使用线程有两种方式:函数或者用类来包装线程对象。 函数式:调用thread模块中的start_new_thread()函数来产生新线程。语法如下: ``` thread.start_new_thread ( function, args[, kwargs] ) ``` 参数说明: * function - 线程函数。 * args - 传递给线程函数的参数,他必须是个tuple类型。 * kwargs - 可选参数。 实例: ``` #!/usr/bin/python import thread import time # 为线程定义一个函数 def print_time( threadName, delay): count = 0 while count < 5: time.sleep(delay) count += 1 print "%s: %s" % ( threadName, time.ctime(time.time()) ) # 创建两个线程 try: thread.start_new_thread( print_time, ("Thread-1", 2, ) ) thread.start_new_thread( print_time, ("Thread-2", 4, ) ) except: print "Error: unable to start thread" while 1: pass ``` 执行以上程序输出结果如下: ``` Thread-1: Thu Jan 22 15:42:17 2009 Thread-1: Thu Jan 22 15:42:19 2009 Thread-2: Thu Jan 22 15:42:19 2009 Thread-1: Thu Jan 22 15:42:21 2009 Thread-2: Thu Jan 22 15:42:23 2009 Thread-1: Thu Jan 22 15:42:23 2009 Thread-1: Thu Jan 22 15:42:25 2009 Thread-2: Thu Jan 22 15:42:27 2009 Thread-2: Thu Jan 22 15:42:31 2009 Thread-2: Thu Jan 22 15:42:35 2009 ``` 线程的结束一般依靠线程函数的自然结束;也可以在线程函数中调用thread.exit(),他抛出SystemExit exception,达到退出线程的目的。 ## 线程模块 Python通过两个标准库thread和threading提供对线程的支持。thread提供了低级别的、原始的线程以及一个简单的锁。 thread 模块提供的其他方法: * threading.currentThread(): 返回当前的线程变量。 * threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 * threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。 除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法: * **run():** 用以表示线程活动的方法。 * **start():**启动线程活动。 * **join([time]):** 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。 * **isAlive():** 返回线程是否活动的。 * **getName():** 返回线程名。 * **setName():** 设置线程名。 ## 使用Threading模块创建线程 使用Threading模块创建线程,直接从threading.Thread继承,然后重写__init__方法和run方法: ``` #!/usr/bin/python import threading import time exitFlag = 0 class myThread (threading.Thread): #继承父类threading.Thread def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): #把要执行的代码写到run函数里面 线程在创建后会直接运行run函数 print "Starting " + self.name print_time(self.name, self.counter, 5) print "Exiting " + self.name def print_time(threadName, delay, counter): while counter: if exitFlag: thread.exit() time.sleep(delay) print "%s: %s" % (threadName, time.ctime(time.time())) counter -= 1 # 创建新线程 thread1 = myThread(1, "Thread-1", 1) thread2 = myThread(2, "Thread-2", 2) # 开启线程 thread1.start() thread2.start() print "Exiting Main Thread" ``` 以上程序执行结果如下; ``` Starting Thread-1 Starting Thread-2 Exiting Main Thread Thread-1: Thu Mar 21 09:10:03 2013 Thread-1: Thu Mar 21 09:10:04 2013 Thread-2: Thu Mar 21 09:10:04 2013 Thread-1: Thu Mar 21 09:10:05 2013 Thread-1: Thu Mar 21 09:10:06 2013 Thread-2: Thu Mar 21 09:10:06 2013 Thread-1: Thu Mar 21 09:10:07 2013 Exiting Thread-1 Thread-2: Thu Mar 21 09:10:08 2013 Thread-2: Thu Mar 21 09:10:10 2013 Thread-2: Thu Mar 21 09:10:12 2013 Exiting Thread-2 ``` ## 线程同步 如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。 使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。如下: 多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。 考虑这样一种情况:一个列表里所有元素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印。 那么,可能线程"set"开始改的时候,线程"print"便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。 锁有两种状态——锁定和未锁定。每当一个线程比如"set"要访问共享数据时,必须先获得锁定;如果已经有别的线程比如"print"获得锁定了,那么就让线程"set"暂停,也就是同步阻塞;等到线程"print"访问完毕,释放锁以后,再让线程"set"继续。 经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。 实例: ``` #!/usr/bin/python import threading import time class myThread (threading.Thread): def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print "Starting " + self.name # 获得锁,成功获得锁定后返回True # 可选的timeout参数不填时将一直阻塞直到获得锁定 # 否则超时后将返回False threadLock.acquire() print_time(self.name, self.counter, 3) # 释放锁 threadLock.release() def print_time(threadName, delay, counter): while counter: time.sleep(delay) print "%s: %s" % (threadName, time.ctime(time.time())) counter -= 1 threadLock = threading.Lock() threads = [] # 创建新线程 thread1 = myThread(1, "Thread-1", 1) thread2 = myThread(2, "Thread-2", 2) # 开启新线程 thread1.start() thread2.start() # 添加线程到线程列表 threads.append(thread1) threads.append(thread2) # 等待所有线程完成 for t in threads: t.join() print "Exiting Main Thread" ``` ## 线程优先级队列( Queue) Python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。 Queue模块中的常用方法: * Queue.qsize() 返回队列的大小 * Queue.empty() 如果队列为空,返回True,反之False * Queue.full() 如果队列满了,返回True,反之False * Queue.full 与 maxsize 大小对应 * Queue.get([block[, timeout]])获取队列,timeout等待时间 * Queue.get_nowait() 相当Queue.get(False) * Queue.put(item) 写入队列,timeout等待时间 * Queue.put_nowait(item) 相当Queue.put(item, False) * Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号 * Queue.join() 实际上意味着等到队列为空,再执行别的操作 实例: ``` #!/usr/bin/python import Queue import threading import time exitFlag = 0 class myThread (threading.Thread): def __init__(self, threadID, name, q): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.q = q def run(self): print "Starting " + self.name process_data(self.name, self.q) print "Exiting " + self.name def process_data(threadName, q): while not exitFlag: queueLock.acquire() if not workQueue.empty(): data = q.get() queueLock.release() print "%s processing %s" % (threadName, data) else: queueLock.release() time.sleep(1) threadList = ["Thread-1", "Thread-2", "Thread-3"] nameList = ["One", "Two", "Three", "Four", "Five"] queueLock = threading.Lock() workQueue = Queue.Queue(10) threads = [] threadID = 1 # 创建新线程 for tName in threadList: thread = myThread(threadID, tName, workQueue) thread.start() threads.append(thread) threadID += 1 # 填充队列 queueLock.acquire() for word in nameList: workQueue.put(word) queueLock.release() # 等待队列清空 while not workQueue.empty(): pass # 通知线程是时候退出 exitFlag = 1 # 等待所有线程完成 for t in threads: t.join() print "Exiting Main Thread" ``` 以上程序执行结果: ``` Starting Thread-1 Starting Thread-2 Starting Thread-3 Thread-1 processing One Thread-2 processing Two Thread-3 processing Three Thread-1 processing Four Thread-2 processing Five Exiting Thread-3 Exiting Thread-1 Exiting Thread-2 Exiting Main Thread ``` # python XML解析 ## 什么是XML? XML 指可扩展标记语言(e**X**tensible **M**arkup **L**anguage)。 你可以通过本站学习[XML教程](/xml/xml-tutorial.html "XML教程") XML 被设计用来传输和存储数据。 XML是一套定义语义标记的规则,这些标记将文档分成许多部件并对这些部件加以标识。 它也是元标记语言,即定义了用于定义其他与特定领域有关的、语义的、结构化的标记语言的句法语言。 ## python对XML的解析 常见的XML编程接口有DOM和SAX,这两种接口处理XML文件的方式不同,当然使用场合也不同。 python有三种方法解析XML,SAX,DOM,以及ElementTree: ### 1.SAX (simple API for XML ) pyhton 标准库包含SAX解析器,SAX用事件驱动模型,通过在解析XML的过程中触发一个个的事件并调用用户定义的回调函数来处理XML文件。 ### 2.DOM(Document Object Model) 将XML数据在内存中解析成一个树,通过对树的操作来操作XML。 ### 3.ElementTree(元素树) ElementTree就像一个轻量级的DOM,具有方便友好的API。代码可用性好,速度快,消耗内存少。 **注:**因DOM需要将XML数据映射到内存中的树,一是比较慢,二是比较耗内存,而SAX流式读取XML文件,比较快,占用内存少,但需要用户实现回调函数(handler)。 本章节使用到的XML实例文件movies.xml内容如下: ``` <collection shelf="New Arrivals"> <movie title="Enemy Behind"> <type>War, Thriller</type> <format>DVD</format> <year>2003</year> <rating>PG</rating> <stars>10</stars> <description>Talk about a US-Japan war</description> </movie> <movie title="Transformers"> <type>Anime, Science Fiction</type> <format>DVD</format> <year>1989</year> <rating>R</rating> <stars>8</stars> <description>A schientific fiction</description> </movie> <movie title="Trigun"> <type>Anime, Action</type> <format>DVD</format> <episodes>4</episodes> <rating>PG</rating> <stars>10</stars> <description>Vash the Stampede!</description> </movie> <movie title="Ishtar"> <type>Comedy</type> <format>VHS</format> <rating>PG</rating> <stars>2</stars> <description>Viewable boredom</description> </movie> </collection> ``` ## python使用SAX解析xml SAX是一种基于事件驱动的API。 利用SAX解析XML文档牵涉到两个部分:解析器和事件处理器。 解析器负责读取XML文档,并向事件处理器发送事件,如元素开始跟元素结束事件; 而事件处理器则负责对事件作出相应,对传递的XML数据进行处理。 <psax适于处理下面的问题:< p=""> * 1、对大型文件进行处理; * 2、只需要文件的部分内容,或者只需从文件中得到特定信息。 * 3、想建立自己的对象模型的时候。 在python中使用sax方式处理xml要先引入xml.sax中的parse函数,还有xml.sax.handler中的ContentHandler。 ### ContentHandler类方法介绍 **characters(content)方法** 调用时机: 从行开始,遇到标签之前,存在字符,content的值为这些字符串。 从一个标签,遇到下一个标签之前, 存在字符,content的值为这些字符串。 从一个标签,遇到行结束符之前,存在字符,content的值为这些字符串。 标签可以是开始标签,也可以是结束标签。 **startDocument()方法** 文档启动的时候调用。 **endDocument()方法** 解析器到达文档结尾时调用。 **startElement(name, attrs)方法** 遇到XML开始标签时调用,name是标签的名字,attrs是标签的属性值字典。 **endElement(name)方法** 遇到XML结束标签时调用。 ## make_parser方法 以下方法创建一个新的解析器对象并返回。 ``` xml.sax.make_parser( [parser_list] ) ``` 参数说明: * **parser_list** - 可选参数,解析器列表 ## parser方法 以下方法创建一个 SAX 解析器并解析xml文档: ``` xml.sax.parse( xmlfile, contenthandler[, errorhandler]) ``` 参数说明: * **xmlfile** - xml文件名 * **contenthandler** - 必须是一个ContentHandler的对象 * **errorhandler** - 如果指定该参数,errorhandler必须是一个SAX ErrorHandler对象 ## parseString方法 parseString方法创建一个XML解析器并解析xml字符串: ``` xml.sax.parseString(xmlstring, contenthandler[, errorhandler]) ``` 参数说明: * **xmlstring** - xml字符串 * **contenthandler** - 必须是一个ContentHandler的对象 * **errorhandler** - 如果指定该参数,errorhandler必须是一个SAX ErrorHandler对象 ## Python 解析XML实例 ``` #!/usr/bin/python import xml.sax class MovieHandler( xml.sax.ContentHandler ): def __init__(self): self.CurrentData = "" self.type = "" self.format = "" self.year = "" self.rating = "" self.stars = "" self.description = "" # 元素开始事件处理 def startElement(self, tag, attributes): self.CurrentData = tag if tag == "movie": print "*****Movie*****" title = attributes["title"] print "Title:", title # 元素结束事件处理 def endElement(self, tag): if self.CurrentData == "type": print "Type:", self.type elif self.CurrentData == "format": print "Format:", self.format elif self.CurrentData == "year": print "Year:", self.year elif self.CurrentData == "rating": print "Rating:", self.rating elif self.CurrentData == "stars": print "Stars:", self.stars elif self.CurrentData == "description": print "Description:", self.description self.CurrentData = "" # 内容事件处理 def characters(self, content): if self.CurrentData == "type": self.type = content elif self.CurrentData == "format": self.format = content elif self.CurrentData == "year": self.year = content elif self.CurrentData == "rating": self.rating = content elif self.CurrentData == "stars": self.stars = content elif self.CurrentData == "description": self.description = content if ( __name__ == "__main__"): # 创建一个 XMLReader parser = xml.sax.make_parser() # turn off namepsaces parser.setFeature(xml.sax.handler.feature_namespaces, 0) # 重写 ContextHandler Handler = MovieHandler() parser.setContentHandler( Handler ) parser.parse("movies.xml") ``` 以上代码执行结果如下: ``` *****Movie***** Title: Enemy Behind Type: War, Thriller Format: DVD Year: 2003 Rating: PG Stars: 10 Description: Talk about a US-Japan war *****Movie***** Title: Transformers Type: Anime, Science Fiction Format: DVD Year: 1989 Rating: R Stars: 8 Description: A schientific fiction *****Movie***** Title: Trigun Type: Anime, Action Format: DVD Rating: PG Stars: 10 Description: Vash the Stampede! *****Movie***** Title: Ishtar Type: Comedy Format: VHS Rating: PG Stars: 2 Description: Viewable boredom ``` 完整的 SAX API 文档请查阅[Python SAX APIs](//docs.python.org/library/xml.sax.html) ## 使用xml.dom解析xml 文件对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展置标语言的标准编程接口。 一个 DOM 的解析器在解析一个 XML 文档时,一次性读取整个文档,把文档中所有元素保存在内存中的一个树结构里,之后你可以利用DOM 提供的不同的函数来读取或修改文档的内容和结构,也可以把修改过的内容写入xml文件。 python中用xml.dom.minidom来解析xml文件,实例如下: ``` #!/usr/bin/python from xml.dom.minidom import parse import xml.dom.minidom # 使用minidom解析器打开 XML 文档 DOMTree = xml.dom.minidom.parse("movies.xml") collection = DOMTree.documentElement if collection.hasAttribute("shelf"): print "Root element : %s" % collection.getAttribute("shelf") # 在集合中获取所有电影 movies = collection.getElementsByTagName("movie") # 打印每部电影的详细信息 for movie in movies: print "*****Movie*****" if movie.hasAttribute("title"): print "Title: %s" % movie.getAttribute("title") type = movie.getElementsByTagName('type')[0] print "Type: %s" % type.childNodes[0].data format = movie.getElementsByTagName('format')[0] print "Format: %s" % format.childNodes[0].data rating = movie.getElementsByTagName('rating')[0] print "Rating: %s" % rating.childNodes[0].data description = movie.getElementsByTagName('description')[0] print "Description: %s" % description.childNodes[0].data ``` 以上程序执行结果如下: ``` Root element : New Arrivals *****Movie***** Title: Enemy Behind Type: War, Thriller Format: DVD Rating: PG Description: Talk about a US-Japan war *****Movie***** Title: Transformers Type: Anime, Science Fiction Format: DVD Rating: R Description: A schientific fiction *****Movie***** Title: Trigun Type: Anime, Action Format: DVD Rating: PG Description: Vash the Stampede! *****Movie***** Title: Ishtar Type: Comedy Format: VHS Rating: PG Description: Viewable boredom ``` 完整的 DOM API 文档请查阅[Python DOM APIs](//docs.python.org/library/xml.dom.html)。 # python GUI编程(Tkinter) python提供了多个图形开发界面的库,几个常用Python GUI库如下: * **Tkinter:** Tkinter模块("Tk 接口")是Python的标准Tk GUI工具包的接口.Tk和Tkinter可以在大多数的Unix平台下使用,同样可以应用在Windows和Macintosh系统里.,Tk8.0的后续版本可以实现本地窗口风格,并良好地运行在绝大多数平台中。 * **wxPython:**wxPython 是一款开源软件,是 Python 语言的一套优秀的 GUI 图形库,允许 Python 程序员很方便的创建完整的、功能键全的 GUI 用户界面。 * **Jython:**Jython程序可以和Java无缝集成。除了一些标准模块,Jython使用Java的模块。Jython几乎拥有标准的Python中不依赖于C语言的全部模块。比如,Jython的用户界面将使用Swing,AWT或者SWT。Jython可以被动态或静态地编译成Java字节码。 ## Tkinter 编程 Tkinter 是Python的标准GUI库。Python使用Tkinter可以快速的创建GUI应用程序。 由于Tkinter是内置到python的安装包中、只要安装好Python之后就能import Tkinter库、而且IDLE也是用Tkinter编写而成、对于简单的图形界面Tkinter还是能应付自如。 创建一个GUI程序 * 1、导入Tkinter模块 * 2、创建控件 * 3、指定这个控件的master, 即这个控件属于哪一个 * 4、告诉GM(geometry manager)有一个控件产生了。 实例: ``` #!/usr/bin/python import Tkinter top = Tkinter.Tk() # 进入消息循环 top.mainloop() ``` 以上代码执行结果如下图: ![tkwindow](https://box.kancloud.cn/2015-12-12_566c100386d23.jpg) ## Tkinter 组件 Tkinter的提供各种控件,如按钮,标签和文本框,一个GUI应用程序中使用。这些控件通常被称为控件或者部件。 目前有15种Tkinter的部件。我们提出这些部件以及一个简短的介绍,在下面的表: | 控件 | 描述 | | --- | --- | | Button | 按钮控件;在程序中显示按钮。 | | Canvas | 画布控件;显示图形元素如线条或文本 | | Checkbutton | 多选框控件;用于在程序中提供多项选择框 | | Entry | 输入控件;用于显示简单的文本内容 | | Frame | 框架控件;在屏幕上显示一个矩形区域,多用来作为容器 | | Label | 标签控件;可以显示文本和位图 | | Listbox | 列表框控件;在Listbox窗口小部件是用来显示一个字符串列表给用户 | | Menubutton | 菜单按钮控件,由于显示菜单项。 | | Menu | 菜单控件;显示菜单栏,下拉菜单和弹出菜单 | | Message | 消息控件;用来显示多行文本,与label比较类似 | | Radiobutton | 单选按钮控件;显示一个单选的按钮状态 | | Scale | 范围控件;显示一个数值刻度,为输出限定范围的数字区间 | | Scrollbar | 滚动条控件,当内容超过可视化区域时使用,如列表框。. | | Text | 文本控件;用于显示多行文本 | | Toplevel | 容器控件;用来提供一个单独的对话框,和Frame比较类似 | | Spinbox | 输入控件;与Entry类似,但是可以指定输入范围值 | | PanedWindow | PanedWindow是一个窗口布局管理的插件,可以包含一个或者多个子控件。 | | LabelFrame | labelframe 是一个简单的容器控件。常用与复杂的窗口布局。 | | tkMessageBox | 用于显示你应用程序的消息框。 | ## 标准属性 标准属性也就是所有控件的共同属性,如大小,字体和颜色等等。 | 属性 | 描述 | | --- | --- | | Dimension | 控件大小; | | Color | 控件颜色; | | Font | 控件字体; | | Anchor | 锚点; | | Relief | 控件样式; | | Bitmap | 位图; | | Cursor | 光标; | ## 几何管理 Tkinter控件有特定的几何状态管理方法,管理整个控件区域组织,一下是Tkinter公开的几何管理类:包、网格、位置 | 几何方法 | 描述 | | --- | --- | | pack() | 包装; | | grid() | 网格; | | place() | 位置; |