Python多线程即相关理念详解

目录
  • 一、什么是线程?
  • 二、开启线程的两种方式
    • 1、方式1
    • 2、方式2
  • 三、线程对象的jion方法()
  • 四、 补充小案例
  • 五、守护线程
  • 六、线程互斥锁
  • 七、GTL-全局解释器
  • 八、验证多线程与多线程运用场景
  • 总结:

一、什么是线程?

线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程。车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线。所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。

总结进程与线程区别:

'''
进程:资源单位
线程:执行单位
线程才是真正干活的人,干活中需要的资源由线程所在进程提供
每个进程肯定自带一个线程
每个进程内可创建多个线程
'''
'''
开进程:
    申请空间
    拷贝代码
    消耗资源大
开线程:
    同一个进程内创建多个线程,无需上述两种操作,消耗资源相对较小
'''

多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。

二、开启线程的两种方式

1、方式1

from threading import Thread
import time
# 方法一
def task(name):
    print('%s is running' % name)
    time.sleep(1)
    print('%s is over' % name)
# 开启线程不需要在main下面执行代码,直接书写就可以
# 但是习惯性的将启动命令写在main下面
t = Thread(target=task, args=('egon',))
t.start()  # 创建线程的开销非常小,几乎是代码一执行就已经创建了
print('主')
'''

运行结果:
egon is running

egon is over
'''

2、方式2

from threading import Thread
class MyThread(Thread):
    def __init__(self, name):
        # 重写了别人的方法,又不知道别人的方法里有啥,就调用父类的方法
        super().__init__()
        self.name = name
    def run(self):
        print('%s is running' % self.name)
        time.sleep(1)
        print('%s is over' % self.name)
if __name__ == '__main__':
    t = MyThread('egon')
    t.start()
    print('主')
'''

运行结果:
egon is running

egon is over
'''

三、线程对象的jion方法()

看过我讲解进程文章的小伙伴想必都知道jion的功能,线程的jion方法于进程的jion方法功能类似-等待一个线程执行完毕后再执行下一个线程

from threading import Thread
def task(name):
    print('%s is running' % name)
    time.sleep(1)
    print('%s is over' % name)
if __name__ == '__main__':
    t=Thread(target=task,args=('egon',))
    t.start()
    t.join()# 主线程等待子线程运行结束后再执行
    print('主')

'''
运行结果:
egon is running
egon is over

'''

补充一个知识点:同一个进程下的多个线程数据共享,下面为大家举一个简单的案例

from threading import Thread
money=100
def task():
    global money
    money=66
if __name__ == '__main__':
    t=Thread(target=task,args=())
    t.start()
    print(money)

# 结果:66

四、 补充小案例

from threading import Thread
import os,time
def task():
    print('子 pid:',os.getpid())
if __name__ == '__main__':
    t=Thread(target=task,args=())
    t.start()
    print('主 pid:',os.getpid())
    # 两个线程的pid号一样,说明在同一个进程下

'''
运行结果:
子 pid: 13444
主 pid: 13444
'''

# 这是个容易混淆的案例
from threading import Thread,current_thread,active_count
import os,time
def task(n):
    print('子',current_thread().name)
    time.sleep(n) # 延长线程存活时间
if __name__ == '__main__':
    t=Thread(target=task,args=(1,))
    t1=Thread(target=task,args=(1,))
    t.start()
    t1.start()
    t.join()
    # print('主',current_thread().name)# 获取线程名字
    print(active_count()) # 统计当前活跃的进程数

'''
运行结果:
子 Thread-1
子 Thread-2
1
'''
# 这里大家容易以为是3,其实运行后只有一个线程在活跃了,其它两个线程运行完后就停止运行了

五、守护线程

守护线程与守护进程的概念也类似,其实大家也能注意到,进程与线程有许多知识点即用法都是相通的,理解了一个另一个也是差不多的道理

1、守护线程会随着主线程的结束而结束

2、主线程运行结束后不会立刻结束,会等待所有的其它非守护线程结束后才会结束

3、因为主线程的结束意味着所在进程的结束

from threading import Thread
import time
def task(name):
    print('%s is running'%name)
    time.sleep(1)
    print('%s is over'%name)
if __name__ == '__main__':
    t=Thread(target=task,args=('egon',))
    t.daemon=True #将t设置为守护线程
    t.start()
    print('主')

'''
运行结果:
egon is running

'''

# 稍微有点迷惑性的例子
from threading import Thread
import time
def foo():
    print('1234')
    time.sleep(1)
    print('end1234')
def func():
    print('5678')
    time.sleep(3)
    print('end5678')
if __name__ == '__main__':
    t1=Thread(target=foo,args=())
    t2=Thread(target=func,args=())
    t1.daemon=True # t1设为守护线程,t2为非守护线程
    t1.start()
    t2.start()
    print('主......')

'''
运行结果:
1234
5678主......
end1234
end5678
'''

'''
因主线程会等待非守护线程运行结束后在结束,
所有主线程会等待t2(非守护线程)结束再结束,
'''

六、线程互斥锁

多个线程操作同一份数据的时候,会出现数据错乱的问题

针对上述问题,解决方式就是加锁处理

from threading import  Thread,Lock
import time
money=100
mutex=Lock()
def task():
    global money
    mutex.acquire()
    tmp=money
    time.sleep(0.1)# 模拟网络延迟
    money=tmp-1
    mutex.release()
if __name__ == '__main__':
    t_list=[]
    for i in range(100):
        t=Thread(target=task,args=())
        t.start()
        t_list.append(t)
    for t in t_list:
        t.join()
    print(money)

# 运行结果:0
# 多个人操作同一份数据,数据错乱,加锁处理

七、GTL-全局解释器

相信学python的小伙伴都知道,python解释器其实有多个版本

  • Cpython
  • Jpython
  • Pypython

但是普遍使用的都是Cpython解释器

在Cpython解释器中GIL是一把互斥锁,用来阻止同一个进程下的多个线程的同时执行

要注意同一进程下的多个线程无法利用多核优势!!!!

想必大家心中也有不少疑惑:pyhon的多线程是不是一点用都没了呢????

因为Cpython中的内存管理不是线程安全的。多线程并不是一无是处的,在遇到多IO操作的时候,多核的优势也会显示不出来,多进程与多线程的效率在该情况下差不了多少,而此时多进程相对浪费资源,多线程更加节省资源

ps:内存管理就是垃圾回收机制:

1、引用计数

2、标记清除

3、分带回收

# GTL-全局解释器
# 重点:1、GIL不是python的特点而是Cpython解释器的特点
#      2、GIL是保证解释器级别的数据的安全
#      3、GIL会导致同一个进程下的多个线程无法同时进行(即无法利用多核优势)
#      4、针对不同的数据还是需要加不同的锁处理
#      5、解释型语言的通病,同一个进程下多个线程无法利用多核优势

多线程是否有用要看具体情况

八、验证多线程与多线程运用场景

# 计算密集型(CPU一直工作,也没有IO)(更适合多进程)
from multiprocessing import Process
from threading import Thread
import os,time
# 多进程情况
def work():
    res=0
    for i in range(0,10000000):
        res*=i
if __name__ == '__main__':
    l=[]
    print(os.cpu_count())# 获取当前计算机CPU核数
    start_time=time.time()
    for i in range(8):# 我计算机是8核
        p= Process(target=work,args=())
        p.start()
        l.append(p)
    for p in l:
        p.join()
    print(time.time()-start_time)

'''
运行结果:
8
2.0726492404937744
'''

# 多线程情况
from multiprocessing import Process
from threading import Thread
import os,time
def work():
    res=0
    for i in range(0,10000000):
        res*=i
if __name__ == '__main__':
    l=[]
    print(os.cpu_count())# 获取当前计算机CPU核数
    start_time=time.time()
    for i in range(8):# 我计算机是8核
        t=Thread(target=work,args=())
        t.start()
        l.append(t)
    for p in l:
        p.join()
    print(time.time()-start_time)

'''
运行结果:
8
3.5790603160858154
'''

# 显然可知:计算密集型更时候多进程

# IO密集型(任务一直有IO)(多线程更合适)
from multiprocessing import Process
from threading import Thread
import os,time
# 多线程
def work():
    time.sleep(1)
if __name__ == '__main__':
    l=[]
    start_time=time.time()
    for i in range(40):
        t=Thread(target=work,args=())
        t.start()
        l.append(t)
    for p in l:
        p.join()
    print(time.time()-start_time)
# 运行结果:1.0205152034759521
# 多进程
from multiprocessing import Process
from threading import Thread
import os,time
def work():
    time.sleep(1)
if __name__ == '__main__':
    l=[]
    start_time=time.time()
    for i in range(40):
        p= Process(target=work,args=())
        # t=Thread(target=work,args=())
        # t.start()
        # l.append(t)
        p.start()
        l.append(p)
    for p in l:
        p.join()
    print(time.time()-start_time)

# 运行结果:5.927189588546753

# 显然可知:IO密集型更适合多线程

总结:

多线程和多进程都各自有各自的优势

并且在后面的项目中通常可以多进程下面再开设多线程

这样的话我们可以利用多核也可以节省资源消耗

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • Python 多线程处理任务实例

    目录 美餐每天发一个用Excel汇总的就餐数据,我们把它导入到数据库后,行政办公服务用它和公司内的就餐数据进行比对查重. 初始实现是单线程,和import_records去掉多线程后的部分差不多. 读取Excel数据 -> 发送到行政服务接口 安全起见线上操作放在了晚上进行.运行时发现每条数据导入消耗1s多,晚上十点开始跑这几千条数据想想都让人崩溃. 等着也是干等,下楼转两圈透透气,屋里龌龊的空气让人昏昏沉沉,寒冷让人清醒不少,突然想到为什么不用多线程呢? 第一版多线程和处理业务的程序糅合在了一

  • 如何利用python多线程爬取天气网站图片并保存

    目录 1.1 题目 1.2 思路 1.2.1 发送请求 1.2.2 解析网页 1.2.3 获取结点 1.2.4 数据保存 (单线程) 1.2.4 数据保存 (多线程) 总结 1.1 题目 指定一个网站,爬取这个网站中的所有的所有图片,例如中国气象网(www.weather.com.cn),分别使用单线程和多线程的方式爬取.(限定爬取图片数量为学号后3位) 输出信息: 将下载的Url信息在控制台输出,并将下载的图片存储在images子文件中,并给出截图. 1.2 思路 1.2.1 发送请求 构造请

  • 深入了解Python的多线程基础

    目录 线程 多线程 Python多线程 创建线程 GIL锁 线程池 总结 线程 线程(Thread),有时也被称为轻量级进程(Lightweight Process,LWP),是操作系统独⽴调度和分派的基本单位,本质上就是一串指令的集合. ⼀个标准的线程由线程id.当前指令指针(PC),寄存器集合和堆栈组成,它是进程中的⼀个实体,线程本身不拥有系统资源,只拥有⼀点⼉在运⾏中必不可少的资源(如程序计数器.寄存器.栈),但它可与同属⼀个进程的其它线程共享进程所拥有的全部资源.线程不能够独⽴执⾏,必须

  • python 多线程与多进程效率测试

    目录 1.概述 2.代码练习 3.运行结果 1.概述 在Python中,计算密集型任务适用于多进程,IO密集型任务适用于多线程 正常来讲,多线程要比多进程效率更高,因为进程间的切换需要的资源和开销更大,而线程相对更小,但是我们使用的Python大多数的解释器是Cpython,众所周知Cpython有个GIL锁,导致执行计算密集型任务时多线程实际只能是单线程,而且由于线程之间切换的开销导致多线程往往比实际的单线程还要慢,所以在 python 中计算密集型任务通常使用多进程,因为各个进程有各自独立的

  • Python实现多线程爬表情包详解

    目录 课程亮点 环境介绍 模块使用 流程 一. 分析我们想要的数据内容 是可以从哪里获取 二. 代码实现步骤 导入模块 单线程爬取10页数据 多进程爬取10页数据 课程亮点 系统分析目标网页 html标签数据解析方法 海量图片数据一键保存 环境介绍 python 3.8 pycharm 模块使用 requests >>> pip install requests parsel >>> pip install parsel time 时间模块 记录运行时间 流程 一. 分

  • Python 多线程超详细到位总结

    目录 多线程threading 线程池 线程互斥 lock与Rlock的区别 在实际处理数据时,因系统内存有限,我们不可能一次把所有数据都导出进行操作,所以需要批量导出依次操作.为了加快运行,我们会采用多线程的方法进行数据处理,以下为我总结的多线程批量处理数据的模板: import threading # 从数据库提取数据的类 class Scheduler(): def __init__(self): self._lock = threading.RLock() self.start = 0

  • Python多线程即相关理念详解

    目录 一.什么是线程? 二.开启线程的两种方式 1.方式1 2.方式2 三.线程对象的jion方法() 四. 补充小案例 五.守护线程 六.线程互斥锁 七.GTL-全局解释器 八.验证多线程与多线程运用场景 总结: 一.什么是线程? 线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程.车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线.所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的

  • python多线程和多进程关系详解

    关于多线程的大概讲解: 在Python的标准库中给出了2个模块:_thread和threading,_thread是低级模块不支持守护线程,当主线程退出了时,全部子线程都会被强制退出了.而threading是高级模块,用作对_thread进行了封装支持守护线程.在大部分状况下人们只需要采用threading这个高级模块即可. 关于多进程的大概讲解: 多进程是multiprocessing模块给出远程与本地的并发,在一个multiprocessing库的采用场景下,全部的子进程全是由一个父进程运行

  • 对python多线程与global变量详解

    今天早上起来写爬虫,基本框架已经搭好,添加多线程爬取功能时,发现出错: 比如在下载文件的url列表中加入200个url,开启50个线程.我的爬虫-竟然将50个url爬取并全部命名为0.html,也就是说,最后的下载结果,是有1个0.html(重复的覆盖了),还有1-150.下面是我的代码: x = str(theguardian_globle.g) #x为给下载的文件命的名 filePath = "E://wgetWeiBao//"+x+".html" try: w

  • Python多线程原理与用法详解

    本文实例讲述了Python多线程原理与用法.分享给大家供大家参考,具体如下: 多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能.具有这种能力的系统包括对称多处理机.多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器.[1] 在一个程序中,这些独立运行的程序片

  • python多线程使用方法实例详解

    本文实例讲述了python多线程使用方法.分享给大家供大家参考,具体如下: threading 模块支持守护线程, 其工作方式是:守护线程一般是一个等待客户端请求服务的服务器. 如果把一个线程设置为守护线程,进程退出时不需要等待这个线程执行完成. 如果主线程准备退出时,不需要等待某些子线程完成,就可以为这些子线程设置守护线程标记. 需要在启动线程之前执行如下赋值语句: thread.daemon = True,检查线程的守护状态也只需要检查这个值即可. 整个 Python 程序将在所有非守护线程

  • python多线程抽象编程模型详解

    最近需要完成一个多线程下载的工具,对其中的多线程下载进行了一个抽象,可以对所有需要使用到多线程编程的地方统一使用这个模型来进行编写. 主要结构: 1.基于Queue标准库实现了一个类似线程池的工具,用户指定提交任务线程submitter与工作线程worker数目,所有线程分别设置为后台运行,提供等待线程运行完成的接口. 2.所有需要完成的任务抽象成task,提供单独的无参数调用方式,供worker线程调用:task以生成器的方式作为参数提供,供submitter调用. 3.所有需要进行线程交互的

  • python 默认参数相关知识详解

    最常见的一种形式是的是为一个或者多个参数指定默认值,这会创建一个可以使用比定义时允许的参数更少的参数调用的函数, def ask_ok(prompt, retries=4, reminder='Please try again!'): while True: ok = input(prompt) if ok in ('y', 'ye', 'yes'): return True if ok in ('n', 'no', 'nop', 'nope'): return False retries =

  • python框架django项目部署相关知识详解

    这篇文章主要介绍了python框架django项目部署相关知识详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一:项目部署的框架 nginx和uWSGI在生产服务器上进行的部署 二:什么是nginx? nginx是一个web服务器. 什么是web服务器? web服务器则主要是让客户可以通过浏览器进行访问,处理HTML文件,css文件,js文件,图片等资源.web服务器一般要处理静态文件.对接服务器. 什么是静态文件? css,js,html

  • Python实现简易Web爬虫详解

    简介: 网络爬虫(又被称为网页蜘蛛),网络机器人,是一种按照一定的规则,自动地抓信息的程序或者脚本.假设互联网是一张很大的蜘蛛网,每个页面之间都通过超链接这根线相互连接,那么我们的爬虫小程序就能够通过这些线不断的搜寻到新的网页. Python作为一种代表简单主义思想的解释型.面向对象.功能强大的高级编程语言.它语法简洁并且具有动态数据类型和高层次的抽象数据结构,这使得它具有良好的跨平台特性,特别适用于爬虫等程序的实现,此外Python还提供了例如Spyder这样的爬虫框架,BeautifulSo

随机推荐