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–>线程 线程是操作系统中能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位. 一个线程指的是进程中一个单一顺序的控制流. 一个进程中可以并发多条线程,每条线程并行 ...
随机推荐
- webpack学习笔记--配置plugins
Plugin Plugin 用于扩展 Webpack 功能,各种各样的 Plugin 几乎让 Webpack 可以做任何构建相关的事情. 配置 Plugin Plugin 的配置很简单, plugi ...
- CentOS6.9安装Kafka
先设置jdk1.8 vi /etc/profile export JAVA_HOME=/usr/local/jdkexport JRE_HOME=/usr/local/jdk/jreexport CL ...
- js高级程序设计
defer 异步脚本,脚本延迟到文档完全被解析和显示之后再执行.只对外部脚本文件有效.按顺序执行脚本.但在实际情况下,并不一定会按照顺序执行最好只有一个延迟脚本.支持H5的浏览器会忽略给脚本设置 de ...
- (4).NET CORE微服务 Micro-Service ---- Consul服务发现和消费
上一章说了 Consul服务注册 现在我要连接上Consul里面的服务 请求它们的API接口 应该怎么做呢? 1.找Consul要一台你需要的服务器 1.1 获取Consul下的所有注册的服务 u ...
- python之string模块常量:数字,26个字母,标点符号,空白
In [8]: import string In [9]: dir(string) In [10]: string.ascii_letters Out[10]: 'abcdefghijklmnopqr ...
- 解决 jQuery 实现父窗口的问题 如window.parent.document.getElementById().innerHTML
因为先前遇到的问题,所以我考虑采用 IFRAME 来隔离不同的脚本,从而实现我需要的效果. 在框架中,我用 JavaScript 获取 JSON 数据,组织成 HTML 代码,最后将其填充至上层文档的 ...
- if-else(职责链)
var a=1,b=2,c=3,d=4; const rules = [ { match: function (a, b, c,d) { return a;}, action: function (a ...
- 启动 ServiceFabric Windows服务报1053
Remote Procedure Call (RPC) Locator和 Windows Firewall是否启动. 以管理员身份运行PowerShell,输入Unregister-Scheduled ...
- Practice| 流程控制
若整数a除以非零整数b,商为整数,且余数为零, 我们就说a能被b整除(或说b能整除a),a为被除数,b为除数,即b|a("|"是整除符号),读作"b整除a"或& ...
- day46 html
老师的笔记: day46 课程安排 HTML CSS JS基础 DOM操作 jQuery Bootstrap pymysql Django基础 项目实战 Vue.js 今日概要: "PUT ...