every blog every motto: Light tomorrow with today.

0. 前言

前不久有一个任务用到了多线程,也简单的学习了下,但是没有做相关记录,今天有空再复习一遍,并简单记录下来。
说明: 后续可能会增补

1. 正文

1.1 基本概念

1>. 单线程&多线程

现在我们有两个任务:烧水和拖地。
单线程: 先烧水,等水烧开,再进行拖地。等待烧水的过程的时间(资源)就白白浪费了。
多线程: 先烧水,烧水的过程(相当于IO操作)把时间(资源)让出来给拖地,地拖完了,水也烧开了。
以上就多线程的优势的直观理解

2>. 线程和进程

  • 一个任务就是一个进程,比如打开浏览器、启动微信等
  • 有些进程同时能做多件事,比如,word可以同时进行打字、拼写检查、打印等,这里多件事,我们称为多个“子任务”,我们把这些“子任务“称为线程
  • 一个程序至少一个进程,一个进程至少一个线程。
  • 多线程可以共享全局变量

3>. 并行&并发

并行(parallel): 是计算机系统中能够同时执行两个或更多个处理的一种计算方法,可以同时工作于同一程序的不同方面,并行处理的主要目的是节省大型和复杂问题的解决时间。

并发(concurrent):同一时间段有几个程序都处于已启动运行和运行完毕之间。且这几个程序都是在同一个处理器(CPU)上运行,但任意时刻上只有一个程序在CPU上运行

并发是指一段时间内宏观上多个程序同时运行,并行是在某一时刻,真正有多个程序运行
小结:

  • 区别:
  1. 并发,多个事情,在同一时间段内同时发生。
  2. 并行,多个事情,在同一时间点上同时发生。
  3. 并发的多个任务之间时互相抢占资源的。
  4. 并行的多个任务之间时不互相抢占资源的
  5. 只有在多CPU或者一个CPU多核的情况中,才会发生并行。否则,看似同时发生的事情,其实都是并发执行的。

4>. 同步&异步

同步异步:
同步: 指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去。
异步: 指进程不需要一直等待下去,而实继续执行下面的操作,不管其他进程的状态,当有消息返回时系统会通知进程进行处理,这样可以提高执行效率

5>. GIL(全局解释锁,Global Interpreter Lock)

GIL并不是python的特性,他是实现python解释器(C时引入的),
作用: 限制多线程同时执行,保证同一时刻只有一个线程执行
原因: 线程并非独立,在一个进程中多个线程共享变量,多个线程执行会导致数据被污染造成数据混乱,这个就是线程不安全性,为此引入互斥锁。
互斥锁: 即确保某段关键代码的数据只能有一个线程从头到尾完整运行,保证了这段代码数据的安全性,但这就导致了死锁。
死锁: 多个子线程在等待对方解除占用状态,但是都不先解锁,互相带灯,这就是死锁。

  1. GIL说白了就是为多线程,一个线程运行其他线程阻塞,使你的多线程代码不是同时执行,而实交替执行。
  2. 基于GIL的存在,在遇到大量IO操作(文件读写、网络等待)时,使用多线程效率更高。

1.2 多线程部分

用装饰器查看函数运行时间,有关装饰器介绍,点我

import time
import functoolsc = 0 # 全局变量def count_time(func):"""装饰器:计算程序运行时间"""@functools.wraps(func)def wrapper(*args, **kwargs):t1 = time.time()res = func(*args, **kwargs)t2 = time.time()# 计算时间time_consumed = t2 - t1print('{}函数一共花费了{}秒'.format(func.__name__, time_consumed))return resreturn wrapper

1.2.1 测试主体为两个函数

两个被测试的函数:

@count_time
def mop_floor(arg):"""拖地操作,"""global cprint('开始拖地……')for i in range(3):time.sleep(1)c += 5c -= 5print('{} is running……c={} !!'.format(arg, c))print('---------地拖完了!-----------')@count_time
def heat_water(arg):"""烧水操作"""global cprint('我要烧水了……')for i in range(4):time.sleep(1)print('{} is running……c={} !!'.format(arg, c))print('------------水烧开了!-------------')

>1. 单线程

单线程主程序:

@count_time
def single_thread():"""单线程程序"""mop_floor('1')heat_water('2')def main():# 单线程single_thread()# my_processing()if __name__ == '__main__':main()

运行结果:
可以看到两个函数依次执行,程序总耗时 7秒

>2. 多线程

@count_time
def my_thread():# mop_floor('1')# heat_water('2')t1 = threading.Thread(target=mop_floor, args=('1',))t2 = threading.Thread(target=heat_water, args=('2',))# t1.setDaemon(True)# t2.setDaemon(True)t1.start()t2.start()t1.join()t2.join()print('-' * 100)def main():# 单线程# single_thread()# 多线程my_thread()if __name__ == '__main__':main()

运行结果:
程序总耗时4秒,可以发现,总的运行时间减少了

说明:
join: join方法让主线程阻塞,等待其创建的线程执行完成

3>. 尝试无join

对多线程主体程序进行修改

@count_time
def my_thread():# mop_floor('1')# heat_water('2')t1 = threading.Thread(target=mop_floor, args=('1',))t2 = threading.Thread(target=heat_water, args=('2',))t1.start()t2.start()print('-' * 100)

结果:
不加join ,当主线程执行完毕之后,当前程序并不会结束,必须等到所有线程都结束之后才能结束当前进程。

4>. 尝试守护线程(daemon)

可以通过将创建的线程指定为守护线程(daemon) ,这样主线程执行完毕之后会立即结束当前为执行的线程,然后结束程序。
守护线程作用: 如果当前python线程是守护线程,那么意味着这个线程是 “不重要” 的,”不重要“意味着如果他的主线程结束了但该守护线程没有运行完,守护线程会被强制结束。

@count_time
def my_thread():# mop_floor('1')# heat_water('2')t1 = threading.Thread(target=mop_floor, args=('1',))t2 = threading.Thread(target=heat_water, args=('2',))t1.setDaemon(True)t2.setDaemon(True)t1.start()t2.start()# t1.join()# t2.join()print('-' * 100)

主线程执行完毕以后会立即结束未执行的线程。

1.2.2 测试主体为一个计算型函数

被测试函数为一个计算型函数

@count_time
def single_func_tested(arg):"""对一个函数进行测试"""sum = 0for i in range(10000000):sum += iprint('{} over'.format(arg))

>1. 单线程

单线程

@count_time
def single_thread():"""单线程程序"""# 对一个函数进行测试single_func_tested('1')print('-'*100)

主函数

def main():# 单线程single_thread()if __name__ == '__main__':main()

结果

>2. 多线程

多线程程序

@count_time
def my_thread():thread_array = {}n = 1for tid in range(n):t = threading.Thread(target=single_func_tested, args=('1',))t.start()thread_array[tid] = tfor i in range(n):thread_array[i].join()print('-' * 100)
def main():# 单线程# single_thread()# 多线程my_thread()# my_processing()if __name__ == '__main__':main()

测试与结果:
当n=1时:

当 n=2 时,

当n=3时,

对单个函数开多线程小结:
由以上可以发现,随着线程数的增加,总运行时间并没有减少反而增加了,同时,由打印结果可以发现,对单个函数开多线程仅是对单个函数重复执行几遍而已

1.2.2 测试主体为一个等待型函数

被测试函数为一个I/O型函数

@count_time
def mop_floor(arg):"""拖地操作,"""global cprint('开始拖地……')for i in range(3):time.sleep(1)c += 5c -= 5print('{} is running……c={} !!'.format(arg, c))print('---------地拖完了!-----------')

>1. 单线程

单线程

@count_time
def single_thread():"""单线程程序"""mop_floor('1')print('-'*100)

主函数

def main():# 单线程single_thread()# 多线程# my_thread()# my_processing()if __name__ == '__main__':main()

>2. 多线程

多线程函数

@count_time
def my_thread():thread_array = {}n = 3for tid in range(n):t = threading.Thread(target=mop_floor, args=('1',))t.start()thread_array[tid] = tfor i in range(n):thread_array[i].join()print('-' * 100)

主函数:

def main():# 多线程my_thread()if __name__ == '__main__':main()


由以上可以发现,当n=3时,运行时间和单线相同(其中,n=1,2结果亦是相同,读者可自行i验证),和上面的单个计算型函数一样,对单个等待型函数也是将其重复执行罢了,但不同的时,单个等待型函数总运行时间和单线程运行时间相同,并未随n的增加而增加。
进一步总结:

  1. 对单个函数开多线程无意义,仅将其重复多遍而已
  2. 个人猜测:
  • 单个计算型函数 对应 CPU密集型
  • 单个等待型函数 对应 I/O密集型
  1. 这篇文章对单个函数测试开多线程,并不能说明问题。

1.2.2 测试主体为一个函数(改进版)

版本一:

@count_time
def single_func_tested(arg):"""对一个函数进行测试"""sum = 0for i in range(100):  # 100000sum += itime.sleep(0.1)for i in range(100):sum += iprint('{} over'.format(arg))

版本二:

@count_time
def single_func_tested(arg):"""对一个函数进行测试"""sum = 0for i in range(100):  # 100000sum += itime.sleep(0.1)for i in range(100):sum += iprint('{} over'.format(arg))

注:

  1. 以上版本,不贴具体结果,由兴趣的读者,可自行尝试。经实验后发现,使用多线程均不能缩短程序运行时间。
  2. 得出对单个程序使用多线程无效(此观点可锤,欢迎)

1.3 源码

https://gist.github.com/onceone/f5489dcc1499c81ee0161f0ea3bb442b

参考文献

[1] https://blog.csdn.net/weixin_39190382/article/details/107107980
[2] http://www.uml.org.cn/python/201901221.asp
[3] https://www.jianshu.com/p/644dbb6d4cc8
[4] https://blog.csdn.net/m0_37324740/article/details/85765167
[5] https://www.cnblogs.com/-qing-/p/11291581.html
[6] https://blog.csdn.net/lzy98/article/details/88819425
[7] https://www.cnblogs.com/yssjun/p/11302500.html
[8] https://www.jb51.net/article/167165.htm
[9] https://blog.csdn.net/weixin_44850984/article/details/89165731
[10] https://www.cnblogs.com/justbreaking/p/7218909.html?utm_source=itdadao&utm_medium=referral

【python】多线程小结相关推荐

  1. python3 多线程_图解|为什么 Python 多线程无法利用多核

    (给Python开发者加星标,提升Python技能) 来源:后端技术指南针 1.全局解释锁 如题: Python的多线程为什么不能利用多核处理器? 全局解释器锁(Global Interpreter ...

  2. Python 多线程抓取网页 牛人 use raw socket implement http request great

    Python 多线程抓取网页 - 糖拌咸鱼 - 博客园 Python 多线程抓取网页 最近,一直在做网络爬虫相关的东西. 看了一下开源C++写的larbin爬虫,仔细阅读了里面的设计思想和一些关键技术 ...

  3. Python 多线程抓取网页

    Python 多线程抓取网页 - 糖拌咸鱼 - 博客园 Python 多线程抓取网页 最近,一直在做网络爬虫相关的东西. 看了一下开源C++写的larbin爬虫,仔细阅读了里面的设计思想和一些关键技术 ...

  4. Python多线程原理与实现

    Date: 2019-06-04 Author: Sun Python多线程原理与实战 目的: (1)了解python线程执行原理 (2)掌握多线程编程与线程同步 (3)了解线程池的使用 1 线程基本 ...

  5. python多线程_干货|理解python多线程和多进程

    点击上方"AI遇见机器学习",选择"星标"公众号 原创干货,第一时间送达 一.多线程与多进程 在介绍Python多线程编程之前,先给大家复习一下进程和线程的概念 ...

  6. python线程延时函数_详解Python 多线程 Timer定时器/延迟执行、Event事件

    Timer继承子Thread类,是Thread的子类,也是线程类,具有线程的能力和特征.这个类用来定义多久执行一个函数. 它的实例是能够延迟执行目标函数的线程,在真正执行目标函数之前,都可以cance ...

  7. 为什么有人说 Python 多线程是鸡肋

    为什么有人会说 Python 多线程是鸡肋?知乎上有人提出这样一个问题,在我们常识中,多进程.多线程都是通过并发的方式充分利用硬件资源提高程序的运行效率,怎么在 Python 中反而成了鸡肋? 有同学 ...

  8. Python多线程(3)——Queue模块

    Python多线程(3)--Queue模块 Queue模块支持先进先出(FIFO)队列,支持多线程的访问,包括一个主要的类型(Queue)和两个异常类(exception classes). Pyth ...

  9. python统计csv行数_对Python 多线程统计所有csv文件的行数方法详解

    如下所示: #统计某文件夹下的所有csv文件的行数(多线程) import threading import csv import os class MyThreadLine(threading.Th ...

  10. c++主线程等待子线程结束_简单明了的 Python 多线程来了 | 原力计划

    作者 | 万里羊责编 | 王晓曼出品 | CSDN博客线程和进程计算机的核心是CPU,它承担了所有的计算任务,就像是一座工厂在时刻运行.如果工厂的资源有限,一次只能供一个车间来使用,也就是说当一个车间 ...

最新文章

  1. 2019百度之星初赛-1
  2. 在linux kernel中netlink的使用示例
  3. linux虚拟机网络查看的方式
  4. JavaSE 基础面试题
  5. mysql7种join连接_mysql 重新整理——七种连接join连接[六]
  6. 22(2)序列化以及反序列化
  7. Linux I/O 模型(待修改)
  8. 康普顿效应是弹性碰撞吗_【量子力学】康普顿散射与逆康普顿散射
  9. Mybatis 与Spring整合及原理
  10. Python代码: 把几个PDF文件拼接为一个 Merge PDF files
  11. Bus hound USTS
  12. t3插密码狗不显示服务器,登陆T3时,用户名和账套都不显示,显示没有检测到合法的LISENCE,需要重新注册密码狗,在注册社区后,搜索.cjt的文件,搜不到...
  13. AI高考的信息检索策略
  14. CoreData 的使用
  15. cryptography
  16. tomcat服务器连接数问题解决
  17. 算法笔记【1】 Kruskal - 克鲁斯卡尔算法
  18. matlab 写word文档,用Matlab生成Word文档
  19. 乔布斯对于flash的看法
  20. BP神经网络Matlab实现(工具箱实现、自主编程实现)

热门文章

  1. c#获取本地ip地址网关子网掩码_C#设置本地网络如DNS、网关、子网掩码、IP等等...
  2. python no such file or directory_Python3 no such file or directory
  3. java的JVM与垃圾回收机制
  4. python自动化读取和写入文件_基于Python的接口自动化读写excel文件的方法
  5. EasyExcel导出excel(写)
  6. 西北农林科技大学c语言上机题题答案,西北农林科技大学C语言上机实习4答案
  7. Visual Studio Code(VS code)简单使用入门以及常用快捷键
  8. SLAM_汇总 | 视觉/激光/多传感器融合SLAM重点知识点
  9. 矩阵运算_Eigen使用_基本数据类型
  10. CVPR2017-图像特征匹配-GMS:基于网格的运动统计的快速且极度鲁棒的图像特征匹配算法