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

python进程池线程异常抛出_python异常处理,多线程,多进程

什么是异常?异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。一般情况下,在Python无法正常处理程序

什么是异常?

异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。

一般情况下,在Python无法正常处理程序时就会发生一个异常。

异常是Python对象,表示一个错误。

当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。

异常处理

捕捉异常可以使用try / except语句。

try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。

如果你不想在异常发生时结束你的程序,只需在try里捕获它。

以下为简单的try....except...else的语法:

try:

正常的操作

......................

except: #可以多次使用except捕捉多个错误

发生异常,执行这块代码

......................

else:  #可有可无

如果没有异常执行这块代码

例:索引错误

l = [1, 2, 3, 4]try:

l[5]exceptIndexError:print('索引错误')else:print('成功')

运行结果:

l = [1, 2, 3, 4]try:

l[5]except IndexError as i: #可以给错误个别名

print(i) #然后打印错误,会出现原生错误

else:print('成功')

运行结果:

try-finally 语句

try-finally 语句无论是否发生异常都将执行最后的代码。

try:

finally:

#退出try时总会执行

raise

例:

l = [1, 2, 3, 4]try:

l[5]exceptIndexError:print('索引错误')finally:print('happy')

运行结果:

自定义异常

通过创建一个新的异常类,程序可以命名它们自己的异常。异常应该是典型的继承自Exception类,通过直接或间接的方式。

以下为与RuntimeError相关的实例,实例中创建了一个类,基类为RuntimeError,用于在异常触发时输出更多的信息。

在try语句块中,用户自定义的异常后执行except块语句,变量 e 是用于创建Networkerror类的实例。

例:

class 异常错误名字(Exception): #自定义异常,继承Exception类的形式

def __init__(self, msg): #初始化

self.message =msg#def __str__(self):

#return self.message

try:raise 异常错误名字('我的异常') #固定格式

except异常错误名字 as e:print(e)

多任务多线程

线程的并发是利用cpu的上下文的切换,在python3里是假的多线程,它不是真真正正的并行,其实就是串行,只不过利用了cpu上下文的切换而已

线程是程序最小的调度单位

例:串行

deftest1():for i in range(3):print('test1=======>%s' %i)deftest2():for i in range(3):print('test2=======>%s' %i)

test1()

test2()

运行结果:

例:并发

import threading #导入线程模块

import time #导入time模块

deftest1():for i in range(3):

time.sleep(1) #为了看出效果,延迟1秒

print('test1=======>%s' %i)deftest2():for i in range(3):

time.sleep(1) #为了看出效果,延迟1秒

print('test2=======>%s' %i)

t1= threading.Thread(target=test1) #实例化一个线程,用Thread方法 ,目标是test1(不加括号,是内存地址)

t2 = threading.Thread(target=test2)

t1.start()#执行这个线程

t2.start() #执行这个线程

运行结果:图一出现后1秒,再次出现两行,如图二,再1秒后,再次出现两行,如图三

多线程执行的顺序是无序的

deftest1(n):

time.sleep(1)print('task', n)for i in range(10):

t= threading.Thread(target=test1, args=('t-%s' % i,)) #传参args,必须元组形式,必须要加逗号

t.start()

运行结果:

多线程共享全局变量

因为函数内得不到函数外的数据,所以要global声明

g_num = 0 #全局变量

defupdate():global g_num #global声明全局变量(才可以修改)

for i in range(10):

g_num+= 1update()print(g_num)

运行结果:

g_num = 0 #全局变量

defupdate():global g_num #global声明全局变量(才可以修改)

for i in range(10):

g_num+= 1

defreader():globalg_numprint(g_num)

t1= threading.Thread(target=update)

t2= threading.Thread(target=reader)

t1.start()

t2.start()

运行结果:

线程是继承在进程里的,没有进程就没有线程

GIL全局解释器锁

GIL的全称是:Global Interpreter Lock,意思就是全局解释器锁,这个GIL并不是python的特性,他是只在Cpython解释器里引入的一个概念,而在其他的语言编写的解释器里就没有这个GIL例如:Jython,Pypy

为什么会有GIL?:

随着电脑多核cpu的出现核cpu频率的提升,为了充分利用多核处理器,进行多线程的编程方式更为普及,随之而来的困难是线程之间数据的一致性和状态同步,而python也利用了多核,所以也逃不开这个困难,为了解决这个数据不能同步的问题,设计了gil全局解释器锁。

例:

deftest1():global global_num #声明全局变量

for i in range(1000000):

global_num+= 1 #加1

print("test1", global_num)deftest2():globalglobal_numfor i in range(1000000):

global_num+= 1

print("test2", global_num)

t1= threading.Thread(target=test1)

t2= threading.Thread(target=test2)

t1.start()

t2.start()## t1.join() #等t1线程执行完毕## t2.join()

print(global_num)

执行结果:会发现每次都是不同的

那么问题来了,为什么每次的执行结果都不一样呢,让我们结合上面的大图,分析一下:

上图和实例解析:

线程共享数据池,就相当于声明的全局变量,线程1拿到公共数据后,首先会申请到gil锁,就是在它进行操作时,不允许其他线程的操作,通过python解释器,调用系统原生线程,,然后假设在cpu上执行,若执行时间到了,线程1 还没有执行结束,那么将会被要求释放gil锁,相当于暂停,释放后线程2会拿到公共数据,同样申请到gil锁,通过python解释器调用原生线程,假设在cpu3上执行,并且完成+1的赋值,全局变量将会从0变成1,并且 ,线程2释放gil锁,线程1再次开始执行,将从暂停的位置开始,当结束+1赋值时,释放gil锁,这里线程1的从0到1赋值的过程将会覆盖掉线程2的赋值,所以,全局变量还是1,接下来还是同样的道理,不管线程1还是线程2,每一次赋值都是覆盖形式的,所以当主线程结束的时候,每次的结果就不一样了。

那么上面的结果并不是我们想要的,针对上面的情况我们的解决的方案是再加一把锁

互斥锁,要么不作,要么做完

GIL全局解释器锁importthreading

global_num=0

lock= threading.Lock() #申请一把锁

deftest1():globalglobal_num

lock.acquire()#上锁

for i in range(1000000):

global_num+= 1lock.release()#释放锁

print("test1", global_num)deftest2():globalglobal_num

lock.acquire()#上锁

for i in range(1000000):

global_num+= 1lock.release()#释放锁

print("test2", global_num)

t1= threading.Thread(target=test1)

t2= threading.Thread(target=test2)

t1.start()

t2.start()

t1.join()#为了防止主线程结束的太快,等t1线程执行完毕

t2.join() #为了防止主线程结束的太快,等t2线程执行完毕

print(global_num)

运行结果:

那么上过锁之后,实际就变成了串行

例:下面代码开了10个进程,跑了多久的时间

importtimedefrun(n):

time.sleep(2)print('this is running======>%s' %n)for i in range(10):

t= threading.Thread(target=run, args=(i,))

t.start()

解决方案:

importtimedefrun(n):

time.sleep(2)print('this is running======>%s' %n)

l= [] #建立一个空列表

start = time.time() #记录开始的时间

for i in range(10):

t= threading.Thread(target=run, args=(i,))

t.start()

l.append(t)#把创建的实例加入列表

for j in l: #循环l列表

j.join() #等待循环进程结束

end = time.time() #记录结束的时间

print('cost', (end-start))

只要在进行耗时的IO操作的时候,能释放GIL,所以只要在IO密集型的代码里,用多线程就很合适

进程

一个程序运行起来之后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单位,不仅可以通过线程完成多任务,进程也是可以的,cpu密集的时候适合用多进程

多进程的并发

importtime

import multiprocessing #导入多重处理模块

deftest1():

for i in range(10):

time.sleep(1)

print('test1====>', i)

deftest2():

for i in range(10):

time.sleep(1)

print('test2====>', i)

if __name__=='__main__': #main的固定格式,如果没有将报错不支持

p1 = multiprocessing.Process(target=test1) #进程1的调用

p2 = multiprocessing.Process(target=test2) #进程2的调用

p1.start()

p2.start()

一这两个进程任务为例,如果cpu核数>=2,那么就是真真正正的 并行

运行结果:

以此类推

进程之间是相互独立的

importmultiprocessing

importtime

g_num =0

def update(): #写函数

globalg_num

for i in range(100):

g_num += 1

print(g_num)

def reader(): #读函数

print(g_num)

if __name__ == '__main__':

p1 = multiprocessing.Process(target=update)

p2 = multiprocessing.Process(target=reader)

p1.start()

p2.start()

运行结果:

,由此看出读函数没有读到写函数更改的内容,所以得出,进程之间互相独立,互不影响

进程池

什么时候用进程池?

当不知道有多少个进程池要跑的时候,用进程池最合适。

importmultiprocessing

from multiprocessing import Pool #或者用 multiprocessing.Pool(),再给他赋变量

importtime

importthreading

g_num =0

deftest1(n):

for i inrange(n):

time.sleep(1)

print('test1', i)

deftest2(n):

for i inrange(n):

time.sleep(1)

print('test2', i)

if __name__ == '__main__':

pool = Pool() #声明进程池 #括号内没有声明进程数,默认无限(根据电脑配置情况)

pool.apply_async(test1, (10,)) #使用方法并且加参数

pool.apply_async(test2, (10,))

pool.close() #进程池关闭

pool.join() #进程池等待(注意的是:join必须放在close后边)

这就是进程池的并发,运行结果:

等等

协程

进程是资源分配的单位,切换需要的资源最大,效率低

线程是操作系统调度的单位,切换需要的资源一般,效率一般

协程是寄生在线程里的,所以协程切换任务资源很小,效率高。IO操作密集的时候首选协程

多进程、多线程根据cpu核数不一样可能是并行的,但是协成在一个线程中

协程,其实就是串行

例:

importgevent,time

deftest1():

for i in range(10):

time.sleep(1) #遇见延迟就切换

print('test1', i)

deftest2():

for i in range(10):

time.sleep(1) #遇见延迟就切换

print('test2', i)

g1 = gevent.spawn(test1) #声明实例,调用函数的方法就是spawn

g2 =gevent.spawn(test2)

g1.join() #调用方式join,不是start

g2.join()

运行结果:依次出现

,由此看出是串行

协程,可以自动切换

由于协程工作时遇见延迟就会切换,所以会在test1,test2来回切换,因为test1延迟1秒,test2,延迟2秒,所以会按照2个test1,一个test2 的规律打印

importgevent,time

#from gevent import monkey #打个补丁支持time.sleep()#monkey.patch_all() #让gevent支持time.sleep()

deftest1():

for i in range(10):

#time.sleep(1) #遇见延迟就切换

gevent.sleep(1) #延迟1秒

print('test1', i)

deftest2():

for i in range(10):

#time.sleep(2) #遇见延迟就切换

gevent.sleep(2) #延迟2秒

print('test2', i)

g1 = gevent.spawn(test1) #声明实例,调用函数的方法就是spawn

g2 =gevent.spawn(test2)

g1.join() #调用方式join,不是start

g2.join()

运行结果:



推荐阅读
  • Python全局解释器锁(GIL)机制详解
    在Python中,线程是操作系统级别的原生线程。为了确保多线程环境下的内存安全,Python虚拟机引入了全局解释器锁(Global Interpreter Lock,简称GIL)。GIL是一种互斥锁,用于保护对解释器状态的访问,防止多个线程同时执行字节码。尽管GIL有助于简化内存管理,但它也限制了多核处理器上多线程程序的并行性能。本文将深入探讨GIL的工作原理及其对Python多线程编程的影响。 ... [详细]
  • Python多线程编程技巧与实战应用详解 ... [详细]
  • 如何优化MySQL数据库性能以提升查询效率和系统稳定性 ... [详细]
  • 尽管我们尽最大努力,任何软件开发过程中都难免会出现缺陷。为了更有效地提升对支持部门的协助与支撑,本文探讨了多种策略和最佳实践,旨在通过改进沟通、增强培训和支持流程来减少这些缺陷的影响,并提高整体服务质量和客户满意度。 ... [详细]
  • 并发编程入门:初探多任务处理技术
    并发编程入门:探索多任务处理技术并发编程是指在单个处理器上高效地管理多个任务的执行过程。其核心在于通过合理分配和协调任务,提高系统的整体性能。主要应用场景包括:1) 将复杂任务分解为多个子任务,并分配给不同的线程,实现并行处理;2) 通过同步机制确保线程间协调一致,避免资源竞争和数据不一致问题。此外,理解并发编程还涉及锁机制、线程池和异步编程等关键技术。 ... [详细]
  • 在Python多进程编程中,`multiprocessing`模块是不可或缺的工具。本文详细探讨了该模块在多进程管理中的核心原理,并通过实际代码示例进行了深入分析。文章不仅总结了常见的多进程编程技巧,还提供了解决常见问题的实用方法,帮助读者更好地理解和应用多进程编程技术。 ... [详细]
  • 本文详细介绍了在MySQL中如何高效利用EXPLAIN命令进行查询优化。通过实例解析和步骤说明,文章旨在帮助读者深入理解EXPLAIN命令的工作原理及其在性能调优中的应用,内容通俗易懂且结构清晰,适合各水平的数据库管理员和技术人员参考学习。 ... [详细]
  • 本文深入解析了JDK 8中HashMap的源代码,重点探讨了put方法的工作机制及其内部参数的设定原理。HashMap允许键和值为null,但键为null的情况只能出现一次,因为null键在内部通过索引0进行存储。文章详细分析了capacity(容量)、size(大小)、loadFactor(加载因子)以及红黑树转换阈值的设定原则,帮助读者更好地理解HashMap的高效实现和性能优化策略。 ... [详细]
  • 为了确保iOS应用能够安全地访问网站数据,本文介绍了如何在Nginx服务器上轻松配置CertBot以实现SSL证书的自动化管理。通过这一过程,可以确保应用始终使用HTTPS协议,从而提升数据传输的安全性和可靠性。文章详细阐述了配置步骤和常见问题的解决方法,帮助读者快速上手并成功部署SSL证书。 ... [详细]
  • 在Cisco IOS XR系统中,存在提供服务的服务器和使用这些服务的客户端。本文深入探讨了进程与线程状态转换机制,分析了其在系统性能优化中的关键作用,并提出了改进措施,以提高系统的响应速度和资源利用率。通过详细研究状态转换的各个环节,本文为开发人员和系统管理员提供了实用的指导,旨在提升整体系统效率和稳定性。 ... [详细]
  • 线程能否先以安全方式获取对象,再进行非安全发布? ... [详细]
  • 在本地环境中部署了两个不同版本的 Flink 集群,分别为 1.9.1 和 1.9.2。近期在尝试启动 1.9.1 版本的 Flink 任务时,遇到了 TaskExecutor 启动失败的问题。尽管 TaskManager 日志显示正常,但任务仍无法成功启动。经过详细分析,发现该问题是由 Kafka 版本不兼容引起的。通过调整 Kafka 客户端配置并升级相关依赖,最终成功解决了这一故障。 ... [详细]
  • 深入解析 Golang 中 Context 的功能与应用
    本文详细探讨了 Golang 中 Context 的核心功能及其应用场景,通过深入解析其工作机制,帮助读者更好地理解和运用这一重要特性,对于提升代码质量和项目开发效率具有重要的参考价值。 ... [详细]
  • 第二章:Kafka基础入门与核心概念解析
    本章节主要介绍了Kafka的基本概念及其核心特性。Kafka是一种分布式消息发布和订阅系统,以其卓越的性能和高吞吐量而著称。最初,Kafka被设计用于LinkedIn的活动流和运营数据处理,旨在高效地管理和传输大规模的数据流。这些数据主要包括用户活动记录、系统日志和其他实时信息。通过深入解析Kafka的设计原理和应用场景,读者将能够更好地理解其在现代大数据架构中的重要地位。 ... [详细]
  • Python 实战:异步爬虫(协程技术)与分布式爬虫(多进程应用)深入解析
    本文将深入探讨 Python 异步爬虫和分布式爬虫的技术细节,重点介绍协程技术和多进程应用在爬虫开发中的实际应用。通过对比多进程和协程的工作原理,帮助读者理解两者在性能和资源利用上的差异,从而在实际项目中做出更合适的选择。文章还将结合具体案例,展示如何高效地实现异步和分布式爬虫,以提升数据抓取的效率和稳定性。 ... [详细]
author-avatar
韩志勇1234
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有