热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

并发编程---守护线程---互斥锁

守护线程守护进程(守护线程)会等待主进程(主线程)运行完毕后被销毁运行完毕并非终止运行:1.对主进程来说:运行完毕指的是主进程

守护线程

        守护进程(守护线程)会等待主进程(主线程)运行完毕后被销毁

        运行完毕并非终止运行:

               1.对主进程来说:运行完毕指的是主进程代码运行完毕

               2.对主线程来说:运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕

       详细解释:

              1.主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵   尸进程),才会结束。

              2.主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程 必须保证非守护线程都运行完毕后才能结束。

from threading import Thread import time def sayhi(name): time.sleep(2) print('%s say hello' %name) if __name__ == '__main__': t=Thread(target=sayhi,args=('egon',)) # t.setDaemon(True) #必须在t.start()之前设置
    t.daemon=True t.start() print('主线程') print(t.is_alive()) ''' 打印结果 主线程 True '''
# 思考下述代码的执行结果有可能是哪些情况?为什么?
from threading import Thread import time def foo(): print(123) time.sleep(1) print("end123") def bar(): print(456) time.sleep(3) print("end456") if __name__ == '__main__': t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True t1.start() t2.start() print("main-------") #必须等到非守护线程执行完毕,才结束
''' 打印结果: 123 456 main------- end123 end456 '''
守护线程

互斥锁

        在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势。

               GIL并不是Python的特性,Python完全可以不依赖于GIL。像其中的JPython就没有GIL。 

        互斥锁: 

               原理:将并行变成串行 

               精髓:局部串行,只针对共享数据修改 保护不同的数据就应该用不用的锁 

''' 互斥锁可以保证程序的安全性提高,但是效率下降 '''
from  threading import Thread,Lock import time n = 100

def task(): global n mutex.acquire() # 99个线程都停在这等待抢锁
    temp = n time.sleep(0.1) # 没加锁之前 睡了0.1秒,100个线程都停到这了
    n=temp-1 mutex.release() if __name__ == '__main__': mutex=Lock() t_l=[] for i in range(100): t = Thread(target=task) t_l.append(t) t.start() for t in t_l: t.join() print('主线程',n) ''' 打印结果: 主线程 0 '''
互斥锁

GIL(global interpreter lock)

   GIL本身就是互斥锁,在一个python的进程内,不仅有test.py的主线程或者由该主线程开启的其他线程,还有解释器开启的垃圾回收等解释器级别的线程,总之,所有线程都运行在这一 个进程内,毫无疑问

 1.所有数据都是共享的,这其中,代码作为一种数据也是被所有线程共享的(test.py的所有代码以及Cpython解释器的所有代码

 例如:test.py定义一个函数work(代码内容如下图),在进程内所有线程都能访问到work的代码,于是我们可以开启三个线程然后target都指向该代码,能访问 到意味着就是可以执行

 2.所有线程的任务,都需要将任务的代码当做参数传给解释器的代码去执行,即所有的线程要想运行自己的任务,首先需要解决的是能够访问到解释器的代码

 执行流程:target=work
多个线程先访问到解释器的代码,即拿到执行权限,然后将target的代码交给解释器的代码去执行
解释器的代码是所有线程共享的,所以垃圾回收线程也可能访问到解释器的代码而去执行,这就导致了一个问题:对于同一个数据100,可能线程1执行x=100的同时,而垃圾回收执行的是回收100的操作,解决这种问题没有什么高明的方法,就是加锁处理,如下图的GIL,保证python解释器同一时间只能执行一个任务的代码

 GIL与Lock

GIL:  是解释器级别的锁,用来保护解释器相关的数据,例如:回收垃圾

Lock: 是应用程序的锁,用来保护应用程序相关的数据

分析:
1.100个线程去抢GIL锁,即抢执行权限 2.肯定有一个线程先抢到GIL(暂且称为线程1),然后开始执行,一旦执行就会拿到lock.acquire() 3.极有可能线程1还未运行完毕,就有另外一个线程2抢到GIL,然后开始运行,但线程2发现互斥锁lock还未被线程1释放,于是阻塞,被迫交出执行权限,即释放GIL 4.直到线程1重新抢到GIL,开始从上次暂停的位置继续执行,直到正常释放互斥锁lock,然后其他的线程再重复2 3 4的过程
GIL与多线程:
1.python的进程可以利用多核,但是开销大;
2.python的多线程开销小,但却无法利用多核的优势;(在python中一个进程中同一时刻只有一个线程执行用不上多核,有GIL锁)
共识:
1、cpu到底是用来做计算的,还是用来做I/O的? 计算的
2、多cpu,意味着可以有多个核并行完成计算,所以多核提升的是计算性能。
3、每个cpu一旦遇到I/O阻塞,仍然需要等待,所以多核对I/O操作没什么用处。
结论:
1、对计算来说,cpu越多越好,但是对于I/O来说,再多的cpu也没用
2、当然对运行一个程序来说,随着cpu的增多执行效率肯定会有所提高(不管提高幅度多大,总会有所提高),这是因为一个程序基本上不会是纯计算或者纯I/O,
所以我们只能相对的去看一个程序到底是计算密集型还是I/O密集型,从而进一步分析python的多线程到底有无用武之地
假设:
有四个任务要处理:玩出并发的效果;
方案:
方案一:开启四个进程
方案二:一个进程下,开启四个线程
单核:
如果四个任务是计算密集型,没有多核来并行计算,方案一徒增了创建进程的开销,方案二胜
如果四个任务是I/O密集型,方案一创建进程的开销大,且进程的切换速度远不如线程,方案二胜
多核:
如果四个任务是计算密集型,多核意味着并行计算,在python中一个进程中同一时刻只有一个线程执行用不上多核,方案一胜
如果四个任务是I/O密集型,再多的核也解决不了I/O问题,方案二胜
结论:
现在的计算机基本上都是多核,python对于计算密集型的任务开多线程的效率并不能带来多大性能上的提升,甚至不如串行(没有大量切换),
但是,对于IO密集型的任务效率还是有显著提升的。
应用:
多线程用于IO密集型,如:socket,爬虫,web
多进程用于计算密集型,如金融分析

# 如果并发的多个任务是计算密集型:多进程效率高
# 如果并发的多个任务是I/O密集型:多线程效率高
#计算密集型:应该用多进程
# from multiprocessing import Process
# from threading import Thread
# import os,time
# def work():
#     res=0
#     for i in range(10000000):
#         res*=i
#
#
# if __name__ == '__main__':
#     l=[]
#     print(os.cpu_count()) #本机为4核
#     start=time.time()
#     for i in range(4):
#         # p=Process(target=work) #耗时3s多
#         p=Thread(target=work) #耗时9s多
#         l.append(p)
#         p.start()
#     for p in l:
#         p.join()
#     stop=time.time()
#     print('run time is %s' %(stop-start))

# IO密集型:用多线程
from multiprocessing import Process
from threading import Thread
import threading
import os,time

def work():
    time.sleep(2)

if __name__ == '__main__':
    l=[]
    print(os.cpu_count()) #本机为4核
    start=time.time()
    for i in range(40):
        # p=Process(target=work) #耗时5.4s多,大部分时间耗费在创建进程上
        p=Thread(target=work) #耗时2.0s多
        l.append(p)
        p.start()
    for p in l:
        p.join()
    stop=time.time()
    print('run time is %s' %(stop-start))

 

 
 
 
 
 

 

      

 


推荐阅读
  • 关于进程的复习:#管道#数据的共享Managerdictlist#进程池#cpu个数1#retmap(func,iterable)#异步自带close和join#所有 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 在Python中,可以通过导入 `this` 模块来优雅地展示“Python之禅”这一编程哲学。该模块会将这些指导原则以一种美观的方式输出到控制台。为了增加趣味性,可以考虑在代码中对输出内容进行简单的加密或混淆处理,以提升用户体验。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • MySQL索引详解与优化
    本文深入探讨了MySQL中的索引机制,包括索引的基本概念、优势与劣势、分类及其实现原理,并详细介绍了索引的使用场景和优化技巧。通过具体示例,帮助读者更好地理解和应用索引以提升数据库性能。 ... [详细]
  • pypy 真的能让 Python 比 C 还快么?
    作者:肖恩顿来源:游戏不存在最近“pypy为什么能让python比c还快”刷屏了,原文讲的内容偏理论,干货比较少。我们可以再深入一点点,了解pypy的真相。正式开始之前,多唠叨两句 ... [详细]
  • 在Python 2.7环境中使用PyCharm进行Cvxopt的安装及线性规划问题求解。具体步骤包括:通过PyCharm的文件菜单进入项目设置,选择解释器选项,点击右侧的“+”按钮,在可用包列表中搜索并安装Cvxopt。安装完成后,可以通过导入Cvxopt库并调用其函数来解决线性规划问题,提高模型的准确性和效率。 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • Python自动化处理:从Word文档提取内容并生成带水印的PDF
    本文介绍如何利用Python实现从特定网站下载Word文档,去除水印并添加自定义水印,最终将文档转换为PDF格式。该方法适用于批量处理和自动化需求。 ... [详细]
  • 本文详细解析了Python中的os和sys模块,介绍了它们的功能、常用方法及其在实际编程中的应用。 ... [详细]
  • 本文介绍了在 Python 3.4 中使用 Pygame 时遇到的导入错误及其解决方案。 ... [详细]
  • 在处理大规模并发请求时,传统的多线程或多进程模型往往无法有效解决性能瓶颈问题。尽管它们在处理小规模任务时能提升效率,但在高并发场景下,系统资源的过度消耗和上下文切换的开销会显著降低整体性能。相比之下,Python 的 `asyncio` 模块通过协程提供了一种轻量级且高效的并发解决方案。本文将深入解析 `asyncio` 模块的原理及其在实际应用中的优化技巧,帮助开发者更好地利用协程技术提升程序性能。 ... [详细]
  • 在Python 3环境中,当无法连接互联网时,可以通过下载离线模块包来实现模块的安装。具体步骤包括:首先从PyPI网站下载所需的模块包,然后将其传输到目标环境,并使用`pip install`命令进行本地安装。此方法不仅适用于单个模块,还支持依赖项的批量安装,确保开发环境的完整性和一致性。 ... [详细]
  • 在Python编程中,当遇到程序运行无响应的问题时,通常与计算资源的消耗有关。Python使用任意精度整数进行计算,这意味着在处理大数值运算时,如计算大指数值,系统可能会因为内存或CPU资源不足而变得缓慢,甚至没有反馈。此外,代码中的无限循环或递归调用也可能导致类似问题。建议检查代码逻辑,优化算法效率,并确保计算任务不会超出系统的处理能力。 ... [详细]
author-avatar
LXY520TB_194
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有