目录

多线程

_thread模块

使用 _thread模块创建线程

threading

使用 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的更多相关文章

  1. Python多线程练习(threading)

    这几天学习python多线程的时候,试了几次thread模块和threading模块,发现thread模块非常的不好用.强烈不建议大家使用thread,建议使用threading模块,此模块对thre ...

  2. Python多线程thread、threading(一)

    Python多线程(一) Python多线程,类似于同时执行多个不同程序,多线程运行的有点: 1.使用线程可以把占据长时间的程序中的任务放到后台去处理 2.用户界面可以更加吸引人,这样比如用户点击了一 ...

  3. Python多线程1:threading

    threading模块提供了高级别的线程接口,基于低级别的_thread模块实现. 模块基本方法 该模块定了的方法例如以下: threading.active_count()         返回当前 ...

  4. python 多线程编程之threading模块(Thread类)创建线程的三种方法

    摘录 python核心编程 上节介绍的thread模块,是不支持守护线程的.当主线程退出的时候,所有的子线程都将终止,不管他们是否仍在工作. 本节开始,我们开始介绍python的另外多线程模块thre ...

  5. python 多线程_thread

    import _thread import time def print_time(threadName, delay, iterations): start = int(time.time()) , ...

  6. 浅析Python多线程

    学习Python多线程的资料很多,吐槽Python多线程的博客也不少.本文主要介绍Python多线程实际应用,且假设读者已经了解多线程的基本概念.如果读者对进程线程概念不甚了解,可参见知名博主 阮一峰 ...

  7. python多线程(一)

    原文:http://www.pythonclub.org/python-basic/threading 一.python多线程thread和threading实现 python是支持多线程的,并且是n ...

  8. Python多线程多进程那些事儿看这篇就够了~~

    自己以前也写过多线程,发现都是零零碎碎,这篇写写详细点,填一下GIL和Python多线程多进程的坑~ 总结下GIL的坑和python多线程多进程分别应用场景(IO密集.计算密集)以及具体实现的代码模块 ...

  9. thread/threading——Python多线程入门笔记

    1 什么是线程? (1)线程不同于程序. 线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制: 多线程类似于同时执行多个不同程序. (2)线程不同于进程. 每个独立的进程有一个程 ...

随机推荐

  1. LeetCode-二叉树的深度

    二叉树的深度 二叉树的深度 使用递归求解二叉树的深度. 需要注意使用的临界条件. /** * 任意一个二叉树的最大深度 **/ #include<iostream> #include< ...

  2. python flask框架详解

    Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务.本文参考自Flask官方文档, 英文不好的同学也可以参考中文文档 1.安装flask pi ...

  3. 从零学脚手架(二)---初识webpack

    在上一篇中,介绍了 webpack 的 entry . output . plugins 属性. 在这一篇,接着介绍其它配置属性. mode 这个属性在上一篇中使用过一次:设置 webpack 编译模 ...

  4. Git代码分支开发工作流程

    本文的工作流程,有一个共同点:都采用"功能驱动式开发"(Feature-driven development,简称FDD). 它指的是,需求是开发的起点,先有需求再有功能分支(fe ...

  5. 使用wireshark 抓取 http https tcp ip 协议进行学习

    使用wireshark 抓取 http https tcp ip 协议进行学习 前言 本节使用wireshark工具抓包学习tcp ip http 协议 1. tcp 1.1 tcp三次握手在wire ...

  6. 最简单的JVM内存结构图

    JVM内存结构图 大家好,好几天没有更新了,今天的内容有点多,我们详细介绍下JVM内部结构图,还是和之前一样,案例先行,方便大家理解记忆. /** * @author :jiaolian * @dat ...

  7. webpack4.x 从零开始配置vue 项目(一)基础搭建项目

    序 现在依旧记得第一次看到webpack3.x 版本配置时候的状态  刚开始看到这些真的是一脸懵.希望这篇文章能帮到刚开始入门的同学. webpack 是什么? webpack是一个模块化打包工具,w ...

  8. 面试现场:说说char 和 varchar的区别你了解多少?

    Hi,大家好!我是白日梦!本文是MySQL专题的第 26 篇. 下文还是白日梦以自导自演的方式,围绕"说说char 和 varchar的区别你了解多少?"展开本话题.看看你能抗到第 ...

  9. python-链队列的实现

    7 class Node(object): 8 def __init__(self,data): 9 self.data = data 10 self.next = None 11 12 class ...

  10. java例题_40 字母字符串转数组后排序

    1 /*40 [程序 40 字符串排序] 输入一个字符串数组,按照字母表的降序对这些字符串进行排序. 2 题目:字符串排序. 3 */ 4 5 /*分析 6 * 1.从键盘得到一个纯字母的字符串 7 ...