Python多线程_thread和Threading
目录
在讲多线程之前,我们先看一个单线程的例子:
import _thread
import time
from datetime import datetime
def Test(name):
for i in range(3):
print(name,datetime.now())
time.sleep(1)
def main():
Test("one")
Test("two")
if __name__=='__main__':
start=time.time()
main()
end=time.time()
print("运行程序花费了%s秒"%(end-start))
##################################################
one 2018-11-11 11:17:57.984571
one 2018-11-11 11:17:58.987543
one 2018-11-11 11:17:59.988000
two 2018-11-11 11:18:00.988495
two 2018-11-11 11:18:01.989449
two 2018-11-11 11:18:02.990809
运行程序花费了6.008334159851074秒
可以看到,对于单线程,运行完这段程序需要6秒的时间。
现在我们开始讲多线程了!
Python3通过两个标准库 _thread 和 threading 提供对线程的支持。
_thread 提供了低级别的、原始的线程以及一个简单的锁,它相比于 threading 模块的功能还是比较有限的。threading 模块除了包含 _thread 模块中的所有方法外,还提供其他方法。所以我们一般常用的是 threading 模块
多线程
多线程类似于同时执行多个不同程序,多线程运行有如下优点:
- 使用线程可以把占据长时间的程序中的任务放到后台去处理。
- 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
- 程序的运行速度可能加快
- 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
线程在执行过程中与进程还是有区别的。每个独立的进程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,线程必须依存在进程中,由应用程序提供多个线程执行控制。
每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了进程上次运行该线程的CPU寄存器的状态。
- 线程可以被抢占(中断)。
- 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让
Python中使用线程有两种方式:函数 或者用 类 来包装线程对象。_thread 模块用的是函数式线程,threading 可以包装线程对象,也可以使用函数式线程
_thread模块(不常用)
函数式:调用 _thread 模块中的 start_new_thread() 函数来产生新线程。语法如下:
_thread.start_new_thread ( function, args [, kwargs] )
参数说明:
- function - 线程函数
- args - 传递给线程函数的参数,他必须是个 tuple 类型
- kwargs - 可选参数
使用 _thread模块创建多线程
对于上面这个例子,我们用多线程来实现,看需要多长的时间
import _thread
import time
from datetime import datetime
def Test(name):
for i in range(3):
print(name,datetime.now())
time.sleep(1)
def main():
_thread.start_new_thread(Test,("one ",))
_thread.start_new_thread(Test,("two ",))
if __name__=='__main__':
start=time.time()
main()
end=time.time()
print("运行程序花费了%s秒"%(end-start))
############################################################
运行程序花费了0.0秒two 2018-11-11 11:30:53.353519
one
2018-11-11 11:30:53.360632
two 2018-11-11 11:30:54.361750
one 2018-11-11 11:30:54.368121
two 2018-11-11 11:30:55.364558
one 2018-11-11 11:30:55.369716
咦,为什么程序花费0.0秒呢?而且是第一个打印呢?原因在于当子线程还在sleep的时候,我们的父线程就已经执行完了。父线程并没有等待说让子线程执行完再执行。所以最后打印程序花费的时间是0.0秒。但是看子线程打印出来的东西我们可以看到,运行程序是花费了3秒时间的,比单线程省了一半的时间。
这里父线程并没有等待说子线程执行完再执行,这里就涉及到了一个阻塞问题。阻塞问题我们在 threading模块中讲解,利用 threading模块中的 join() 方法来阻塞。
threading
threading 模块除了包含 _thread 模块中的所有方法外,还提供其他方法:
- threading.currentThread(): 返回当前的线程变量。
- threading.enumerate(): 返回一个包含正在运行的线程的list列表。正在运行指线程启动后、结束前。
- threading.activeCount(): 返回正在运行的线程数量,与 len(threading.enumerate()) 有相同的结果。
除了使用方法外,threading 线程模块同样提供了 threading.Thread 类来处理线程,threading.Thread类提供了以下方法:
- run(): 用以表示线程活动的方法。
- start():启动线程活动。
- join([time]): 父线程等待子线程至中止再执行。这阻塞调用线程直至线程的join() 方法被调用中止、正常退出或者抛出未处理的异常、再或者是可选的超时发生。
- isAlive(): 返回线程是否活动的。
- getName(): 返回线程名。
- setName(): 设置线程名。
使用 threading模块创建多线程
我们可以通过创建一个子类继承于 threading.Thread ,并实例化后调用 start() 方法启动新线程,即它调用了线程的 run() 方法:
我们这里使用了 join()方法来阻塞父线程,让他等待子线程运行完再运行,可以看到,最后我们花费了3秒的时间,是单线程的一半。
import threading
import time
from datetime import datetime
class myThread(threading.Thread):
def __init__(self,name):
threading.Thread.__init__(self)
self.name=name
def run(self):
for i in range(3):
print(self.name,datetime.now())
time.sleep(1)
def main():
thread1=myThread("one ")
thread2=myThread("two ")
thread1.start() #启动线程,代用了该线程的 run() 方法
thread2.start()
thread1.join() #必须等子线程运行完了父线程才可以运行
thread2.join()
if __name__=='__main__':
start=time.time()
main()
end=time.time()
print("运行程序花费了%s秒"%(end-start))
###########################################
one 2018-11-11 16:14:50.546845
two 2018-11-11 16:14:50.554877
one 2018-11-11 16:14:51.555091
two 2018-11-11 16:14:51.563160
one 2018-11-11 16:14:52.556510
two 2018-11-11 16:14:52.564490
运行程序花费了3.0182247161865234秒
我们也可以使用函数来创建多线程
import threading
import time
from datetime import datetime
def Test(name):
for i in range(3):
print(name,datetime.now())
time.sleep(1)
def main():
t1=threading.Thread(target=Test,args=("one",)) #调用threading.Thread函数,target参数是要执行的函数,args是要传入的参数,为元组类型
t2=threading.Thread(target=Test,args=("two",))
t1.start() #启动线程
t2.start()
t1.join() #必须等子线程运行完了父线程才可以运行
t2.join()
if __name__=='__main__':
start=time.time()
main()
end=time.time()
print("运行程序花费了%s秒"%(end-start))
##########################################################
one 2018-11-11 16:18:54.727638
two 2018-11-11 16:18:54.735688
one 2018-11-11 16:18:55.736727
two 2018-11-11 16:18:55.743748
one 2018-11-11 16:18:56.740741
two 2018-11-11 16:18:56.746846
运行程序花费了3.0212459564208984秒
线程同步
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。
使用 threading 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire() 方法和 release() 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire() 和 release() 方法之间。如下:
多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。
考虑这样一种情况:
一个列表里所有元素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印。
那么,可能线程"set"开始改的时候,线程"print"便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。
锁有两种状态——锁定和未锁定。每当一个线程比如"set"要访问共享数据时,必须先获得锁定;如果已经有别的线程比如"print"获得锁定了,那么就让线程"set"暂停,也就是同步阻塞;等到线程"print"访问完毕,释放锁以后,再让线程"set"继续。
经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。
实例:
import threading
import time
from datetime import datetime
threadLock=threading.Lock() #得到一个threadLock对象
def Test(name):
threadLock.acquire() ##获取锁,用于线程同步
for i in range(3):
print(name,datetime.now())
time.sleep(1)
threadLock.release() #释放锁,开启下一个线程
def main():
t1=threading.Thread(target=Test,args=("one",)) #调用threading.Thread函数,target参数是要执行的韩三户,args是要传入的参数,为元组类型
t2=threading.Thread(target=Test,args=("two",))
t1.start() #启动线程
t2.start()
t1.join() #必须等子线程运行完了父线程才可以运行
t2.join()
if __name__=='__main__':
start=time.time()
main()
end=time.time()
print("运行程序花费了%s秒"%(end-start))
##################################################################
one 2018-11-11 16:21:42.749223
one 2018-11-11 16:21:43.757153
one 2018-11-11 16:21:44.761436
two 2018-11-11 16:21:45.764834
two 2018-11-11 16:21:46.788177
two 2018-11-11 16:21:47.791732
运行程序花费了6.047051906585693秒
从上面代码可以看出,对函数进行了线程锁定。这样当我们的线程1和线程2都同时调用该函数时, 必须等线程1执行完了线程2再执行,这样,程序花费的时间就相当于单线程时候的6秒了。
Python多线程_thread和Threading的更多相关文章
- Python多线程练习(threading)
这几天学习python多线程的时候,试了几次thread模块和threading模块,发现thread模块非常的不好用.强烈不建议大家使用thread,建议使用threading模块,此模块对thre ...
- Python多线程thread、threading(一)
Python多线程(一) Python多线程,类似于同时执行多个不同程序,多线程运行的有点: 1.使用线程可以把占据长时间的程序中的任务放到后台去处理 2.用户界面可以更加吸引人,这样比如用户点击了一 ...
- Python多线程1:threading
threading模块提供了高级别的线程接口,基于低级别的_thread模块实现. 模块基本方法 该模块定了的方法例如以下: threading.active_count() 返回当前 ...
- python 多线程编程之threading模块(Thread类)创建线程的三种方法
摘录 python核心编程 上节介绍的thread模块,是不支持守护线程的.当主线程退出的时候,所有的子线程都将终止,不管他们是否仍在工作. 本节开始,我们开始介绍python的另外多线程模块thre ...
- python 多线程_thread
import _thread import time def print_time(threadName, delay, iterations): start = int(time.time()) , ...
- 浅析Python多线程
学习Python多线程的资料很多,吐槽Python多线程的博客也不少.本文主要介绍Python多线程实际应用,且假设读者已经了解多线程的基本概念.如果读者对进程线程概念不甚了解,可参见知名博主 阮一峰 ...
- python多线程(一)
原文:http://www.pythonclub.org/python-basic/threading 一.python多线程thread和threading实现 python是支持多线程的,并且是n ...
- Python多线程多进程那些事儿看这篇就够了~~
自己以前也写过多线程,发现都是零零碎碎,这篇写写详细点,填一下GIL和Python多线程多进程的坑~ 总结下GIL的坑和python多线程多进程分别应用场景(IO密集.计算密集)以及具体实现的代码模块 ...
- thread/threading——Python多线程入门笔记
1 什么是线程? (1)线程不同于程序. 线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制: 多线程类似于同时执行多个不同程序. (2)线程不同于进程. 每个独立的进程有一个程 ...
随机推荐
- 还在用crontab? 分布式定时任务了解一下
前言 日常任务开放中,我们会有很多异步.批量.定时.延迟任务要处理,go-zero中有 go-queue,推荐使用 go-queue 去处理,go-queue 本身也是基于 go-zero 开发的,其 ...
- JS五种绑定彻底弄懂this,默认绑定、隐式绑定、显式绑定、new绑定、箭头函数绑定详解(转载)
目录 壹 ❀ 引 贰 ❀ this默认绑定 叁 ❀ this隐式绑定 1.隐式绑定 2.隐式丢失 肆 ❀ this显式绑定 伍 ❀ new绑定 陆 ❀ this绑定优先级 柒 ❀ 箭头函数的this ...
- NIO三大组件之Buffer
什么是Buffer Buffer(这里并不是特指Buffer类)是一个存储数据的容器,与数组类似(其实底层依旧是用数组的结构来存储数据),但不同的是,Buffer对象提供了一组更有效的方法去进行写入和 ...
- 《进击吧!Blazor!》系列入门教程 第一章 7.图表
<进击吧!Blazor!>是本人与张善友老师合作的Blazor零基础入门教程视频,此教程能让一个从未接触过Blazor的程序员掌握开发Blazor应用的能力. 视频地址:https://s ...
- sqli-labs系列——第三关
less3 判断注入类型 这第三关有点意思,是一个带括号的数字型注入,这里需要闭合它的括号,之前遇到过很多这样的站,它的sql语句一般都是这样的: $sql = select * from user ...
- github文件快速下载
目录 一,提升加载速度 二,提升下载速度 只是想快速下载文件的直接看第二部分. github加载速度慢究其原因还是伟大的墙的存在.我们需要赞美墙,但就算墙很伟大,问题还是要解决的. 有问题就解决问题, ...
- 每天自学两小时Python,整理了最详细的学习路线和规
上次这篇文章每天自学两小时Python,三个月学通月入20K主要是给大家整理了学习资料视频和PDF书籍,很多需要的都关注私信领取了. 很多朋友领取之后都问我教程有了那么应该从哪去开始学习呢,私信太多我 ...
- PAT (Advanced Level) Practice 1023 Have Fun with Numbers (20 分) 凌宸1642
PAT (Advanced Level) Practice 1023 Have Fun with Numbers (20 分) 凌宸1642 题目描述: Notice that the number ...
- SqlServer游标的创建与使用
前言 大家都对SqlServer视图.存储过程.触发器的创建与使用有一定的了解了,我们来看下什么是游标,怎么使用,什么时候用. SqlServer视图的创建与使用 SqlServer存储过程的创建与使 ...
- [图论]剑鱼行动:prim
剑鱼行动 目录 剑鱼行动 Description Input Output Sample Input Sample Output 解析 难点 代码 Description 给出N个点的坐标,对它们建立 ...