python之进程和线程2
1 GIL全局解释器锁定义
定义:在一个线程拥有了解释器的访问权后,其他的所有线程都必须等待他释放解释器的访问权,即这些线程的下一条指令并不会互相影响。
缺点:多处理器退化为单处理器
优点:避免大量的加锁解锁操作
无论你启多少个线程,你有多少个cpu,python在执行一个进程的时候会淡定的在同一时刻只允许一个线程运行。
Python是无法利用多核cpu实现多线程的
总结:
对于计算密集型任务,python的多线程并没有用
对于IO密集型任务,python的多线程是有意义的
python使用多核:开进程,弊端:开销大而且切换复杂
着重点:协程+多进程
方向:IO多路复用
终极思路:换C模块实现多线程
2 同步锁
锁通常被用来实现对共享资源的同步访问,为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其他线程已经获得了该锁,则当前线程需等待其被释放),待资源访问完,再调用release方法释放锁:
import time
import threading def addNum():
global num #在每个线程中都获取这个全局变量
#num-=1 #获取的值为0 与temp=num num=temp-1是同一个意思 唯一在于time.sleep()
lock.acquire()#添加锁
temp=num
time.sleep(0.001)#IO操作 如果时间在0.1时,足够让一条线程完成后再走另一条线程 取值从100开始,99
#如果时间为0.001时,100条线程运行时出现不确定性,有可能走到第五条的时候就完成IO操作,前五条基数为100,第六条基数就是99
num =temp-1 # 对此公共变量进行-1操作
lock.release()#解锁
num = 100 #设定一个共享变量 thread_list = []
lock=threading.Lock()#获取一把锁
for i in range(100):
t = threading.Thread(target=addNum)
t.start()
thread_list.append(t) for t in thread_list: #等待所有线程执行完毕
t.join() print('Result: ', num)
3 死锁和递归锁
死锁就是指两个或者两个以上进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,他们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁这些永远在互相等待的进程被称为死锁进程。
import threading
import time mutexA = threading.Lock()
mutexB = threading.Lock() class MyThread(threading.Thread): def __init__(self):
threading.Thread.__init__(self) def run(self):
self.fun1()
self.fun2() def fun1(self): mutexA.acquire() # 如果锁被占用,则阻塞在这里,等待锁的释放 print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time())) mutexB.acquire()
print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
mutexB.release() mutexA.release() def fun2(self): mutexB.acquire()
print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
time.sleep(0.2) mutexA.acquire()
print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
mutexA.release() mutexB.release() if __name__ == "__main__": print("start---------------------------%s"%time.time()) for i in range(0, 10):
my_thread = MyThread()
my_thread.start()
递归锁
Python中为了支持在同一线程中多次请求统一资源,python提供了可重入锁RLock。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acpuire都被release,其他的线程才能获得资源。
递归锁用来解决死锁
import threading
import time
Rlock=threading.RLock()#生成一个递归锁
class MyThread(threading.Thread): def __init__(self):
threading.Thread.__init__(self) def run(self):
self.fun1()
self.fun2() def fun1(self):
Rlock.acquire() # 一条线程运行,其他线程等待 count=1 print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time())) Rlock.acquire()#同一条线程运行,其他线程等待中。。。count=2,递归锁
print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
Rlock.release()#同一线程运行完成后,count=1 Rlock.release()#同一线程运行完成后,count=0 ,其他线程才能获得资源 def fun2(self):
Rlock.acquire()
print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time())) Rlock.acquire()
print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
Rlock.release() Rlock.release() if __name__ == "__main__": print("start---------------------------%s"%time.time()) for i in range(0, 10):
my_thread = MyThread()
my_thread.start()
4 Event对象
线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得棘手,为了解决这些问题,我们需要使用threading库中的Event对象。对象包含一个可由线程设置的信号标志,他允许线程等待某些事件的发生。
在初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象,而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么他将忽略这个事件,继续执行
event对象
线程之间的简单通信
event.set()更改标志位为True,
所有阻塞池的线程激活进入就绪状态, 等待操作系统调度
event.wait()等待;还可以接收一个超时参数,默认情况下超过这个参数设定的值之后,wait方法会返回
event.clear()恢复event的状态值为False
event.isSet():返回event的状态值
右边的线程需要左边线程运行的时候,用Event.set来更改状态,为True;
如果右边的线程状态一直为默认为假,左边的线程则一直等待,不会执行。
应用场景:
我们有多个线程从Redis队列中读取数据来处理,这些线程都要尝试去连接Redis的服务,一般情况下,如果Redis连接不成功,在各个线程的代码中,都会去尝试重新连接。如果我们想要在启动时确保Redis服务正常,才让那些工作线程去连接Redis服务器,那么我们就可以采用threading.Event机制来协调各个工作线程的连接操作:主线程中会去尝试连接Redis服务,如果正常的话,触发事件,各工作线程会尝试连接Redis服务。
import threading
import time
import logging logging.basicConfig(level=logging.DEBUG, format='(%(threadName)-10s) %(message)s',) def worker(event):
logging.debug('Waiting for redis ready...')
event.wait()#由于状态默认为假,需等待
logging.debug('redis ready, and connect to redis server and do some work [%s]', time.ctime())
time.sleep(1) def main():#主函数
readis_ready = threading.Event()
t1 = threading.Thread(target=worker, args=(readis_ready,), name='t1')#产生一个对象
t1.start()#开始运行 t2 = threading.Thread(target=worker, args=(readis_ready,), name='t2')
t2.start()#产生一个对象 logging.debug('first of all, check redis server, make sure it is OK, and then trigger the redis ready event')
time.sleep(3) # simulate the check progress
readis_ready.set()#更改状态为True,默认为False if __name__=="__main__":
main()
5 Semaphore(信号量)
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
实例:(同时只有5个线程可以获得semaphore,即可以限制最大连接数为5):
import threading
import time
semaphore = threading.Semaphore(5)
def func():
semaphore.acquire()
print (threading.currentThread().getName() + ' get semaphore')
time.sleep(2)
semaphore.release()
for i in range(20):#总数为20,最大连接为5,分为4次
t1 = threading.Thread(target=func)
t1.start()
python之进程和线程2的更多相关文章
- Python的进程与线程--思维导图
Python的进程与线程--思维导图
- Python创建进程、线程的两种方式
代码创建进程和线程的两种方式 """ 定心丸:Python创建进程和线程的方式基本都是一致的,包括其中的调用方法等,学会一个 另一个自然也就会了. "" ...
- python之进程与线程
什么是操作系统 可能很多人都会说,我们平时装的windows7 windows10都是操作系统,没错,他们都是操作系统.还有没有其他的? 想想我们使用的手机,Google公司的Androi ...
- python的进程与线程(二)
线程 之前了解了操作系统的发展史,也知道了进程和线程的概念,归纳一下就是: 进程:本质上就是一段程序的运行过程(抽象的概念) 线程:最小的执行单元,是进程的实体 ...
- Python 9 进程,线程
本节内容 python GIL全局解释器锁 线程 进程 Python GIL(Global Interpreter Lock) In CPython, the global interpreter l ...
- python之进程和线程
1 操作系统 为什么要有操作系统 ? 操作系统位于底层硬件与应用软件之间的一层 工作方式:向下管理硬件,向上提供接口 操作系统进程切换: 出现IO操作 固定时间 2 进程和线程的概念 进程就是一个程序 ...
- 《Python》进程收尾线程初识
一.数据共享 from multiprocessing import Manager 把所有实现了数据共享的比较便捷的类都重新又封装了一遍,并且在原有的multiprocessing基础上增加了新的机 ...
- Python基础进程和线程
一 背景知识 进程的概念起源于操作系统,是操作系统最核心的概念. 进程是对正在运行程序的一个抽象,操作系统的其他所有内容都是围绕进程的概念展开的.所以想要真正了解进程,必须事先了解操作系统,egon介 ...
- Python中进程和线程的总体区别
Num01–>线程 线程是操作系统中能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位. 一个线程指的是进程中一个单一顺序的控制流. 一个进程中可以并发多条线程,每条线程并行 ...
随机推荐
- linux 出错 “INFO: task xxxxxx: 634 blocked for more than 120 seconds.”的3种解决方案
https://blog.csdn.net/electrocrazy/article/details/79377214
- .Net Core下发送WebRequest请求的两种方式
1.使用RestSharp.NetCore 2.使用WebApi请求方式
- 键盘Hook【Delphi版】
原文:https://www.cnblogs.com/edisonfeng/archive/2012/05/18/2507858.html 一.钩子的基本概念 a) Hook作用:监视windows消 ...
- [转]Oh My Zsh,安装,主题配置
https://swp-song.com/2017/08/20/Tools/OhMyZsh%E5%AE%89%E8%A3%85%E5%92%8C%E4%B8%BB%E9%A2%98%E9%85%8D% ...
- python全栈开发day77-博客主页
1.文章分类 2.标签 3.归档 1) MySQL的日期格式化函数 DATE_FORMAT(字段名,格式) 2) Django ORM中如何执行SQL原生语句 (1) models.Article.o ...
- JMeter实现登录初始化(类似LR的init函数功能实现)
1.项目背景 在做项目的性能测试过程中,发现系统的登录功能非常慢,所以,在涉及到登录才能操作的场景,尽量避开登录操作 解决方案: 首选设置“登录并生成签名值”线程组
- windows server远程连接提示“终端服务器超出了最大允许连接”
- lvs介绍
1Linux集群及系统扩展的方式概述 集群是有多台服务器组织在一起,一起工作,因为单台服务器的并发响应能力是有限的,响应处理能力也是有限的所有有了集群的出现 在系统扩展有2种方法: 1 向上扩展:是指 ...
- To the Max POJ - 1050 (最大子段和)
Given a two-dimensional array of positive and negative integers, a sub-rectangle is any contiguous s ...
- Java Lambda基础——Function, Consumer, Predicate, Supplier, 及FunctionalInterface接口
这几个接口经常与Lambda结合使用,网上当然也有很多介绍,不过有些过于繁琐,有些又偏简单,秉着实用主义精神,今天这里折中一下,把介绍的内容分为两部分,第一部分相当于TLDR,总结几个"口诀 ...