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

python2个子线程等待_python并发编程之多线程2死锁与递归锁,信号量等...

一、死锁现象与递归锁进程也是有死锁的所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作

一、死锁现象与递归锁

进程也是有死锁的

所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,

它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,

如下就是死锁

1 死锁-------------------

2 from threading importThread,Lock,RLock3 importtime4 mutexA =Lock()5 mutexB =Lock()6 classMyThread(Thread):7 defrun(self):8 self.f1()9 self.f2()10 deff1(self):11 mutexA.acquire()12 print('\033[33m%s 拿到A锁'%self.name)13 mutexB.acquire()14 print('\033[45%s 拿到B锁'%self.name)15 mutexB.release()16 mutexA.release()17 deff2(self):18 mutexB.acquire()19 print('\033[33%s 拿到B锁' %self.name)20 time.sleep(1) #睡一秒就是为了保证A锁已经被别人那到了

21 mutexA.acquire()22 print('\033[45m%s 拿到B锁' %self.name)23 mutexA.release()24 mutexB.release()25 if __name__ == '__main__':26 for i in range(10):27 t =MyThread()28 t.start() #一开启就会去调用run方法

死锁现象

那么怎么解决死锁现象呢?

解决方法,递归锁:在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。

这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。

直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁

mutexA=mutexB=threading.RLock() #一个线程拿到锁,counter加1,该线程内又碰到加锁的情况,

则counter继续加1,这期间所有其他线程都只能等待,等待该线程释放所有锁,即counter递减到0为止

1 #2.解决死锁的方法--------------递归锁

2 from threading importThread,Lock,RLock3 importtime4 mutexB = mutexA =RLock()5 classMyThread(Thread):6 defrun(self):7 self.f1()8 self.f2()9 deff1(self):10 mutexA.acquire()11 print('\033[33m%s 拿到A锁'%self.name)12 mutexB.acquire()13 print('\033[45%s 拿到B锁'%self.name)14 mutexB.release()15 mutexA.release()16 deff2(self):17 mutexB.acquire()18 print('\033[33%s 拿到B锁' %self.name)19 time.sleep(1) #睡一秒就是为了保证A锁已经被别人拿到了

20 mutexA.acquire()21 print('\033[45m%s 拿到B锁' %self.name)22 mutexA.release()23 mutexB.release()24 if __name__ == '__main__':25 for i in range(10):26 t =MyThread()27 t.start() #一开启就会去调用run方法

解决死锁

二、信号量Semaphore(其实也是一把锁)

Semaphore管理一个内置的计数器

Semaphore与进程池看起来类似,但是是完全不同的概念。

进程池:Pool(4),最大只能产生四个进程,而且从头到尾都只是这四个进程,不会产生新的。

信号量:信号量是产生的一堆进程/线程,即产生了多个任务都去抢那一把锁

1 from threading importThread,Semaphore,currentThread2 importtime,random3 sm = Semaphore(5) #运行的时候有5个人

4 deftask():5 sm.acquire()6 print('\033[42m %s上厕所'%currentThread().getName())7 time.sleep(random.randint(1,3))8 print('\033[31m %s上完厕所走了'%currentThread().getName())9 sm.release()10 if __name__ == '__main__':11 for i in range(20): #开了10个线程 ,这20人都要上厕所

12 t = Thread(target=task)13 t.start()

Semaphore举例

1 hread-1上厕所2 Thread-2上厕所3 Thread-3上厕所4 Thread-4上厕所5 Thread-5上厕所6 Thread-3上完厕所走了7 Thread-6上厕所8 Thread-1上完厕所走了9 Thread-7上厕所10 Thread-2上完厕所走了11 Thread-8上厕所12 Thread-6上完厕所走了13 Thread-5上完厕所走了14 Thread-4上完厕所走了15 Thread-9上厕所16 Thread-10上厕所17 Thread-11上厕所18 Thread-9上完厕所走了19 Thread-12上厕所20 Thread-7上完厕所走了21 Thread-13上厕所22 Thread-10上完厕所走了23 Thread-8上完厕所走了24 Thread-14上厕所25 Thread-15上厕所26 Thread-12上完厕所走了27 Thread-11上完厕所走了28 Thread-16上厕所29 Thread-17上厕所30 Thread-14上完厕所走了31 Thread-15上完厕所走了32 Thread-17上完厕所走了33 Thread-18上厕所34 Thread-19上厕所35 Thread-20上厕所36 Thread-13上完厕所走了37 Thread-20上完厕所走了38 Thread-16上完厕所走了39 Thread-18上完厕所走了40 Thread-19上完厕所走了

运行结果

三、Event

线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行

from threading import Event

Event.isSet() #返回event的状态值

Event.wait() #如果 event.isSet()==False将阻塞线程;

Event.set() #设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;

Event.clear() #恢复

例如1.,有多个工作线程尝试链接MySQL,我们想要在链接前确保MySQL服务正常才让那些工作线程去连接MySQL服务器,如果连接不成功,都会去尝试重新连接。那么我们就可以采用threading.Event机制来协调各个工作线程的连接操作

1 #首先定义两个函数,一个是连接数据库

2 #一个是检测数据库

3 from threading importThread,Event,currentThread4 importtime5 e =Event()6 defconn_mysql():7 '''链接数据库'''

8 count = 1

9 while not e.is_set(): #当没有检测到时候

10 if count >3: #如果尝试次数大于3,就主动抛异常

11 raise ConnectionError('尝试链接的次数过多')12 print('\033[45m%s 第%s次尝试'%(currentThread(),count))13 e.wait(timeout=1) #等待检测(里面的参数是超时1秒)

14 count+=1

15 print('\033[44m%s 开始链接...'%(currentThread().getName()))16 defcheck_mysql():17 '''检测数据库'''

18 print('\033[42m%s 检测mysql...' %(currentThread().getName()))19 time.sleep(5)20 e.set()21 if __name__ == '__main__':22 for i in range(3): #三个去链接

23 t = Thread(target=conn_mysql)24 t.start()25 t = Thread(target=check_mysql)26 t.start()

详看

2.例如2,红绿灯的例子

1 from threading importThread,Event,currentThread2 importtime3 e =Event()4 deftraffic_lights():5 '''红绿灯'''

6 time.sleep(5)7 e.set()8 defcar():9 '''车'''

10 print('\033[42m %s 等绿灯\033[0m'%currentThread().getName())11 e.wait()12 print('\033[44m %s 车开始通行' %currentThread().getName())13 if __name__ == '__main__':14 for i in range(10):15 t = Thread(target=car) #10辆车

16 t.start()17 traffic_thread = Thread(target=traffic_lights) #一个红绿灯

18 traffic_thread.start()

红绿灯

四、定时器(Timer)

指定n秒后执行某操作

from threading importTimerdeffunc(n):print('hello,world',n)

t= Timer(3,func,args=(123,)) #等待三秒后执行func函数,因为func函数有参数,那就再传一个参数进去

t.start()

五、线程queue

queue队列 :使用import queue,用法与进程Queue一样

queue.Queue(maxsize=0) #先进先出

1 #1.队列-----------

2 importqueue3 q = queue.Queue(3) #先进先出

4 q.put('first')5 q.put('second')6 q.put('third')7 print(q.get())8 print(q.get())9 print(q.get())

View Code

queue.LifoQueue(maxsize=0)#先进后出

1 #2.堆栈----------

2 q = queue.LifoQueue() #先进后出(或者后进先出)

3 q.put('first')4 q.put('second')5 q.put('third')6 q.put('for')7 print(q.get())8 print(q.get())9 print(q.get())

View Code

queue.PriorityQueue(maxsize=0) #存储数据时可设置优先级的队列

1 #----------------

2 '''3.put进入一个元组,元组的第一个元素是优先级3 (通常也可以是数字,或者也可以是非数字之间的比较)4 数字越小,优先级越高'''

5 q =queue.PriorityQueue()6 q.put((20,'a'))7 q.put((10,'b')) #先出来的是b,数字越小优先级越高嘛

8 q.put((30,'c'))9 print(q.get())10 print(q.get())11 print(q.get())

View Code

六、多线程性能测试

1.多核也就是多个CPU

(1)cpu越多,提高的是计算的性能

(2)如果程序是IO操作的时候(多核和单核是一样的),再多的cpu也没有意义。

2.实现并发

第一种:一个进程下,开多个线程

第二种:开多个进程

3.多进程:

优点:可以利用多核

缺点:开销大

4.多线程

优点:开销小

缺点:不可以利用多核

5多进程和多进程的应用场景

1.计算密集型:也就是计算多,IO少

如果是计算密集型,就用多进程(如金融分析等)

2.IO密集型:也就是IO多,计算少

如果是IO密集型的,就用多线程(一般遇到的都是IO密集型的)

下例子练习:

1 #计算密集型的要开启多进程

2 from multiprocessing importProcess3 from threading importThread4 importtime5 defwork():6 res =07 for i in range(10000000):8 res+=i9 if __name__ == '__main__':10 l =[]11 start =time.time()12 for i in range(4):13 p = Process(target=work) #1.9371106624603271 #可以利用多核(也就是多个cpu)

14 #p = Thread(target=work) #3.0401737689971924

15 l.append(p)16 p.start()17 for p inl:18 p.join()19 stop =time.time()20 print('%s'%(stop-start))

计算密集型

1 #I/O密集型要开启多线程

2 from multiprocessing importProcess3 from threading importThread4 importtime5 defwork():6 time.sleep(3)7 if __name__ == '__main__':8 l =[]9 start =time.time()10 for i in range(400):11 #p = Process(target=work) #34.9549994468689 #因为开了好多进程,它的开销大,花费的时间也就长了

12 p = Thread(target=work) #2.2151265144348145 #当开了多个线程的时候,它的开销小,花费的时间也小了

13 l.append(p)14 p.start()15 for i inl :16 i.join()17 stop =time.time()18 print('%s'%(stop-start))

I/O密集型

七、python标准模块----concurrent.futures



推荐阅读
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了软件测试知识点之数据库压力测试方法小结相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 第七课主要内容:多进程多线程FIFO,LIFO,优先队列线程局部变量进程与线程的选择线程池异步IO概念及twisted案例股票数据抓取 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
author-avatar
井爱3053_170
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有