Python多线程,python多线程菜鸟教程
简介多线程类似于同时执行多个不同的程序。多线程具有以下优势:
使用线程可以将程序中占用很长时间的任务放到后台处理。用户界面可以更有吸引力。例如,用户点击一个按钮来触发某些事件的处理,可以弹出一个进度条来显示处理进度。可以加快程序的运行速度。线程在一些等待任务的实现上更有用,比如用户输入、文件读写、网络数据收发等。在这种情况下,我们可以释放一些宝贵的资源如内存占用等等。(用户在等待程序响应结束的同时,后台在执行释放内存占用的任务。)每个独立的线程都有一个程序运行的入口,一个顺序执行的序列和一个程序的出口。但是线程不能独立执行,所以必须由应用程序提供的多个线程来控制。
每个线程都有自己的一组CPU寄存器,称为线程的上下文。这个上下文反映了上一次线程的CPU寄存器的状态。
指令指针和堆栈指针寄存器是线程上下文中最重要的两个寄存器,线程总是运行在进程get上下文中。这些地址用于标记拥有该线程的进程的地址空间中的内存。
线程可以被抢占(中断)。当其他线程正在运行时,线程可以暂停(也称为睡眠)——这是线程的特权。螺纹可分为:
内核:由操作系统的内核创建和撤销。用户线程:在没有内核支持的用户程序中实现的线程。Python 3线程中两个常用的模块是:
_threadthreading(推荐)[注意]
线程模块已被放弃。用户可以使用线程模块来代替。因此,在Python3中不能再使用线程模块。为了兼容,Python3将thread重命名为_thread
线程、进程、进程和线程:进程:对于一个操作系统来说,一个任务就是一个进程,比如打开一个浏览器就意味着启动一个浏览器进程。
线程:有些进程同时做多件事,比如Word,可以同时打字、拼写检查、打印。在一个流程中,要同时做很多事情,需要同时运行多个《子任务》。我们在进程线程中称这些为《子任务》。
与线程和进程的区别:简而言之,一个程序至少有一个进程;一个进程至少有一个线程。
进程是应用程序在处理器上的执行过程。这是一个动态的概念,线程是进程的一部分。一个进程包含多个正在运行的线程。
多线程可以共享全局变量,多进程不能。在多线程中,所有子线程都有相同的进程号;在多个流程中,不同的子流程有不同的流程号。
如何理解线程:线程可以是python程序,也可以是python文件中的函数。如果一个python程序或函数开始执行,你也可以说线程开始执行。
并行与并发:并行处理:是一种可以在计算机系统中同时执行两个或两个以上进程的计算方法。并行处理可以同时在同一程序的不同方面工作。并行处理的主要目的是节省解决大型复杂问题的时间。
同一CPU同时执行多个线程;多个CPU,同时执行多个进程(进程与CPU一一对应)]
并发性:是指几个程序在一段时间内处于启动和运行的过程中,直到它们结束,并且这些程序都运行在同一个处理器(CPU)上,但任何时候都只有一个程序运行在该处理器(CPU)上。
[相同的cpu,相同的时间间隔(时间窗口),执行多个线程]
总结:
Python的多线程原理是并发性
Python多线程:在python中,同一时间同一CPU中只能运行一个线程;对于数据安全,引入了全局解释锁(GIL),相当于一张通行证;没有GIL的线程不会进入CPU运行。
Python多线程原理:一个程序运行,其他程序不运行;当正在运行的线程需要等待时(如网络、IO等。),线程暂停【pass (GIL)被拿走】等待,其他线程争夺Gil先得到GIL的线程,然后运行它。
【暂停和争夺GIL的操作是系统调度的,我们不需要管理;当一个线程挂起,竞争,另一个线程开始执行,这个过程会花一点时间,叫做切换时间]
【如果切换时间等待,多线程提高效率;如果切换时间和等待时间差不多,就没必要用python多线程了]
同步和异步同步:当流程执行请求时,如果请求需要一段时间才能返回信息,流程将一直等待,直到收到返回的信息。
异步:是指进程不需要一直等待,而是继续执行下面的操作。不管其他进程的状态如何,当有消息返回时,系统都会通知该进程进行处理,这样可以提高执行效率。
主进程和子进程:线程封装的函数。Thread==子进程;其他部分==主流程
子线程实际上是一个并发任务(在同一时间段内执行的多个操作);
这些任务通常被写入一个func或func类,然后分两步,如下面的代码示例所示:
thread=threading . thread(target=func func,args=(parameter 1,parameter 2))# Package(encapsulate)with thread class
Thread.start() #start然后开始运行setDaemon(Ture):将子进程设置为守护进程==当主进程关闭时,子进程会立即关闭【当你觉得有些线程不重要时,可以设置守护进程线程。】
Join (): Set Blocking==主线程只能在子进程完成后执行[当一些任务必须在其他任务之前完成时可以使用]
对于非守护进程线程,即使主线程完成执行,也需要等待非守护进程完成后才能退出。
单线程:在很多年前的MS-DOS时代,操作系统在处理问题时是单任务的。比如我想做两件事,听音乐和看电影,那么我必须先安排好顺序。
从时间导入ctime,睡眠
def music():
对于范围(2)中的I:
我在听音乐。%s %ctime()
睡眠(1)
def电影():
对于范围(2)中的I:
打印我在看电影!%s %ctime()
睡眠(5)
if __name__==__main__ :
音乐()
电影()
print over % s % ctime()我们先听一段音乐,通过一个for循环控制音乐播放两次。每首音乐播放时间为1秒,sleep()控制音乐播放时长。然后我们又看了一部电影。
每部电影5秒,因为太好看了,所以我也通过for循环看了两遍。整个娱乐活动结束后,我通过print all over %s %ctime()查看了一下当前时间,差不多该睡觉了。
运行结果:
==========================重启========================
我在听音乐。2014年4月17日星期四10时47分08秒
我在听音乐。2014年4月17日星期四10时47分09秒
我在看电影!2014年4月17日星期四10时47分10秒
我在看电影!2014年4月17日星期四10时47分15秒
2014年04月17日10: 47: 20以上其实music()函数和move()函数更应该算是音乐和视频播放器。至于放什么歌,什么视频,要等我们用的时候再决定。
所以对上面的代码做适当的修改:
#编码=utf-8
导入线程
从时间导入ctime,睡眠
定义音乐(func):
对于范围(2)中的I:
print 我正在收听%s. %s %(func,ctime())
睡眠(1)
定义移动(函数):
对于范围(2)中的I:
打印“我在%s!%s %(func,ctime())
睡眠(5)
if __name__==__main__ :
音乐(你热爱商业)
移动(u Avatar )
“在%s上全部打印”%ctime()运行结果:
========================重启=======================
我在听爱情商业。2014年4月17日星期四11时48分59秒
我在听爱情商业。2014年4月17日星期四11:49:00
我在阿凡达那里!2014年4月17日星期四11时49分01秒
我在阿凡达那里!2014年4月17日星期四11时49分06秒
over Thu Apr 17 2014 11:49:11多线程1:在1:Python: functions中使用线程有两种方式,或者用类包装线程对象。
1.Functional:调用_thread模块中的start _ new _thread()函数,生成一个新线程。语法如下:
_ thread . start _ new _ thread(function,args [,kwargs])参数说明:
函数-线程函数。args-传递给线程函数的参数必须是元组类型。kwargs-可选参数。示例:
# -*-编码:utf-8 -*-
导入线程
导入时间
#为线程定义一个函数
def print_time(线程名,延迟):
计数=0
当计数5:
time.sleep(延迟)
计数=1
print(%s: %s % (threadName,time.ctime(time.time())))
#创建两个线程
尝试:
_ Thread . start _ new _ Thread(print _ time,( Thread-1 ,2,))
_ Thread . start _ new _ Thread(print _ time,( Thread-2 ,4,))
除了:
打印(“错误:无法启动线程”)
而1:
通过结果:执行上述过程后,可以按ctrl-c退出。
线程化线程模块Python3通过两个标准库_thread和Threading提供对线程的支持。
_thread提供了一个低级的、原始的线程和一个简单的锁。与线程模块相比,其功能仍然有限。
除了_thread模块中的所有方法,线程模块还提供了其他方法:
Threading.currentThread():返回当前线程变量。Threading.enumerate():返回包含正在运行的线程的列表。运行是指开始后结束前的线程,不包括开始前和结束后的线程。Threading.activeCount():返回正在运行的线程数,结果与len(threading.enumerate())相同。除了使用方法之外,线程模块还提供线程类来处理线程。Thread类提供了以下方法:
Run():用于指示线程活动的方法。Start():启动线程活动。Join([time]):等到线程终止。这将阻塞调用线程,直到调用线程的join()方法来中止(通常会退出或抛出未处理的异常)或者发生可选的超时。IsAlive():返回线程是否处于活动状态。GetName():返回线程名称。Set():设置线程名称。使用线程模块创建线程,我们可以通过直接继承线程来创建一个新的子类。线程,实例化后调用start()方法启动新线程;也就是说,它调用线程模块中Thread类的start()方法:
导入线程
导入时间
exitFlag=0
类myThread(线程。线程):
def __init__(self,threadID,name,counter):
threading.Thread.__init__(self)
self.threadID=threadID
self.name=name
self .计数器=计数器
定义运行(自身):
Print(开始线程: self.name )
print_time(自我名称,自我计数器,5)
Print(退出线程: self.name )
def print_time(线程名、延迟、计数器):
当计数器:
如果exitFlag:
threadName.exit()
time.sleep(延迟)
print(%s: %s % (threadName,time.ctime(time.time())))
计数器=1
#创建一个新线程
thread1=myThread(1, Thread-1 ,1)
thread2=myThread(2, Thread-2 ,2)
#开始一个新线程
thread1.start()
thread2.start()
thread1.join()
thread2.join()
打印(退出主线程)运行结果:
同步如果多个线程一起修改某个数据,可能会出现意想不到的结果。为了保证数据的正确性,需要同步多个线程。
利用线程对象的Lock和Rlock可以实现简单的线程同步,两者都有获取方法和释放方法。对于一次只需要一个线程操作的数据,其操作可以放在获取和释放方法之间。如下所示:
多线程的好处是可以同时运行多个任务(至少感觉是这样)。但是,当线程需要共享数据时,可能会出现数据不同步的情况。
考虑一个情况,一个列表中所有元素都是0,线程‘set’从后向前把所有元素都改成1,线程‘print’负责从前向后读取列表并打印。
那么,可能线程"设置"开始改的时候,线程打印便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。
锁有两种状态——锁定和未锁定。每当一个线程比如"设置"要访问共享数据时,必须先获得锁定;如果已经有别的线程比如打印获得锁定了,那么就让线程"设置"暂停,也就是同步阻塞;等到线程打印访问完毕,释放锁以后,再让线程"设置"继续。
经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半一的尴尬场面。
实例:
导入线程
导入时间
类myThread(线程。线程):
def __init__(self,threadID,name,counter):
threading.Thread.__init__(self)
self.threadID=threadID
self.name=name
自我。计数器=计数器
定义运行(自身):
打印(开启线程: 自我名)
# 获取锁,用于线程同步
threadLock.acquire()
打印时间(自我名称,自我计数器,3)
# 释放锁,开启下一个线程
threadLock.release()
定义打印时间(线程名、延迟、计数器):
当计数器:
时间.睡眠(延迟)
print (%s: %s % (threadName,time.ctime(time.time())))
计数器=1
线程锁=线程。锁定()
线程=[]
# 创建新线程
thread1=myThread(1, Thread-1 ,1)
thread2=myThread(2, Thread-2 ,2)
# 开启新线程
thread1.start()
thread2.start()
# 添加线程到线程列表
线程.追加(线程1)
线程.追加(线程2)
# 等待所有线程完成
对于螺纹中的t:
t.join()
打印(退出主线程)运行结果:
线程优先级队列(队列)Python的长队模块中提供了同步的、线程安全的队列类,包括先进先出(先入先出)队列队列,后进先出(后入先出)队列LifoQueue,和优先级队列优先级队列。
这些队列都实现了锁原语,能够在多线程中直接使用,可以使用队列来实现线程间的同步。
长队模块中的常用方法:
Queue.qsize():返回队列的大小Queue.empty():如果队列为空,返回没错,反之FalseQueue.full():如果队列满了,返回没错,反之错误队列。完整与最大允许的上传大小大小对应排队。获取([块[,超时]]):获取队列,超时等待时间Queue.get_nowait():相当Queue.get(False)Queue.put(item):写入队列,超时等待时间Queue.put_nowait(item):相当Queue.put(item,False)Queue.task_done()。在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号Queue.join():实际上意味着等到队列为空,再执行别的操作实例:
导入队列
导入线程
导入时间
exitFlag=0
类myThread(线程。线程):
def __init__(self,threadID,name,q):
threading.Thread.__init__(self)
self.threadID=threadID
self.name=name
self.q=q
定义运行(自身):
打印(开启线程:自我名)
流程数据(自己的名字,自己的问题)
打印(退出线程:自我名)
def process_data(threadName,q):
不存在时,标记:
queueLock.acquire()
如果不是workQueue.empty():
data=q.get()
queueLock.release()
打印( %s处理% s“%(线程名,数据))
否则:
queueLock.release()
时间。睡眠(1)
threadList=[Thread-1 , Thread-2 , Thread-3]
名称列表=[一,二,三,四,五]
队列锁=线程。锁定()
工作队列=队列。队列(10)
线程=[]
threadID=1
# 创建新线程
对于线程列表中的tName:
thread=myThread(threadID,tName,workQueue)
thread.start()
线程.追加(线程)
threadID=1
# 填充队列
queueLock.acquire()
对于名称列表中的单词:
workQueue.put(word)
queueLock.release()
# 等待队列清空
而不是workQueue.empty():
及格
# 通知线程是时候退出
exitFlag=1
# 等待所有线程完成
对于螺纹中的t:
t.join()
打印(退出主线程)运行结果:
2多线程技术在发展,时代在进步,我们的CPU越来越快。CPU抱怨有更大的东西占用了我一定的时间。其实我同时做多份工作都没问题;于是,操作系统进入了多任务时代。我们听音乐,吃火锅,这是一个梦想。
继续改造上面的例子,引入threadring同时播放音乐和视频:
#编码=utf-8
导入线程
从时间导入ctime,睡眠
定义音乐(func):
对于范围(2)中的I:
print 我正在收听%s. %s %(func,ctime())
睡眠(1)
定义移动(函数):
对于范围(2)中的I:
打印“我在%s!%s %(func,ctime())
睡眠(5)
线程=[]
t1=threading . thread(target=music,args=(u love business ,))
线程.追加(t1)
T2=threading . thread(target=move,args=(u avatar ,))
线程.追加(t2)
if __name__==__main__ :
对于螺纹中的t:
t.setDaemon(True)
启动()
“在%s上全部打印”%ctime()运行结果:
=========================重启=======================
我在听爱情商业。2014年4月17日星期四12时51分45秒
我在阿凡达那里!2014年4月17日星期四12时51分45秒
从执行结果来看,子线程(muisc()、movie())和主线程(printallover%s%ctime())都是同时启动的,但是由于主线程执行完毕,子线程也被终止。
继续调整程序:
.
if __name__==__main__ :
对于螺纹中的t:
t.setDaemon(True)
启动()
对于螺纹中的t:
t.join()
print over % s % ctime()我们只在上面的程序中添加了一个join()方法来等待线程终止。
join()的作用是子线程的父线程会被阻塞,直到子线程结束运行。
注意:join()方法的位置在for循环之外,也就是说,在执行主进程之前,必须等待for循环中的两个进程都完成。
运行结果:
=========================重启=======================
我在听爱情商业。2014年4月17日星期四13:04:11我在阿凡达!2014年4月17日星期四13时04分11秒
我在听爱情商业。2014年4月17日星期四13时04分12秒
我在阿凡达那里!2014年4月17日星期四13时04分16秒
从执行结果中可以看出,音乐()和电影()是同时启动的。
启动时间4分11秒,调用主进程需要10秒。比单线程减少了2秒,我们可以把音乐的睡眠()的时间调整到4秒。
.
定义音乐(func):
对于范围(2)中的I:
print 我正在收听%s. %s %(func,ctime())
睡眠(4)
.执行结果:
======================重启========================
我在听爱情商业。2014年4月17日星期四13:11:27我在阿凡达!2014年4月17日星期四13时11分27秒
我在听爱情商业。2014年4月17日星期四13时11分31秒
我在阿凡达那里!2014年4月17日星期四13时11分32秒
2014年4月17日13: 11: 37,子线程启动11分27秒,主线程运行11分37秒。
虽然音乐的每首歌从1秒延长到了4秒,但是多遍线运行脚本的总时间并没有变化。
说明:导入线程#首先导入线程模块,这是使用多线程的前提。
线程=[]
t1=threading . thread(target=music,args=(u love business ,))
#创建线程数组,创建线程t1,并使用线程。Thread()方法。在这个方法中,调用了music方法target=music,args方法传递了music参数。将创建的线程t1加载到线程数组中。
线程.追加(t1)
#然后用同样的方法创建线程t2,并将t2加载到线程数组中。
T2=threading . thread(target=move,args=(u avatar ,))
线程.追加(t2)
#最后,通过for循环遍历数组。(数组加载了两个线程t1和t2)
对于螺纹中的t:
t.setDaemon(True)
启动()
#setDaemon(True)将线程声明为守护进程,必须在调用start()方法之前进行设置。如果它没有被设置为守护进程,程序将被无限期挂起。子线程启动后,父线程也继续执行。当父线程执行最后一条语句print all over %s %ctime()时,它不等待子线程就退出,子线程也同时结束。
setDaemon()
#开始线程活动。
开始()期待陌生,拥抱惊喜。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。