Python的GIL锁
全局解释器锁
- 全局解释器锁,是计算机程序设计语言解释器用于同步线程的一种机制,它使得任何时刻仅有一个线程在执行即便在多核心处理器上,使用GIL的解释器也只允许同一时间执行一个线程。常见的GIL的解释器有CPython与Ruby MRI
import time
def start():
i=0
for i in range(10000000):
i+=1
return
def main():
s=time.time()
for i in range(10):
start()
print(time.time()-s)
main()
import threading
import time
def start():
i=0
for i in range(10000000):
i+=1
return
def main():
ts={}
s=time.time()
for i in range(10):
t=threading.Thread(target=start)
t.start()
ts[i]=t
#t.join()
#start()
for i in range(10):
ts[i].join()
print(time.time()-s)
if __name__=='__main__':
main()
'''
#5.812853813171387单线程
#5.460023880004883多线程,并未快多少
线程并发不如单线程顺序执行快,
这是得不偿失的,造成这种情况的就是GIL
,这里是计算密集型,所以不适用
'''
- 多线程并发计算密集型一个线程大多数时间在阻塞
- 多线程在IO密集(输入、输出#爬虫大多时就是)的时候有不错的性能
多线程以及非守护线程
import threading
import time
def start():
time.sleep(1)
print(threading.current_thread().name)
print(threading.current_thread().isAlive())
print(threading.current_thread().ident)
print('start')
t=threading.Thread(target=start,name='my first thread')
t.start()
print('stop')
'''
start
stop
my first thread
True
9548
'''
import threading
import time
def start():
time.sleep(1)
print(threading.current_thread().name)
print(threading.current_thread().is_alive())
print(threading.current_thread().ident)
print('start')
t=threading.Thread(target=start,name='my first thread')
t.start()
t.join() #join会阻塞线程,直到线程运行完毕。
print('stop')
'''
start
my first thread
True
1028
stop
'''
- 主线程会跳过创建的线程继续执行
- 知道创建的线程运行完毕
- 程序结束
多线程以及守护线程
import threading
import time
def start():
time.sleep(1)
print(threading.current_thread().name)
print(threading.current_thread().is_alive())
print(threading.current_thread().ident)
print('start')
t=threading.Thread(target=start,name='my first thread')
t.setDaemon(True)
t.start()
print('stop')
'''
start
stop
'''
#用setDaemon(True)设置守护线程,守护线程会伴随主线程一起结束
#,但是也受join阻塞线程约束
LOCK锁
'''
两个线程操作同一个数字,
最后得到的数字是混乱的
'''
import threading
number=0
def addNumber():
global number
for i in range(10000000):
number+=1
def downNumber():
global number
for i in range(10000000):
number-=1
print('start')
t=threading.Thread(target=addNumber)
t2=threading.Thread(target=downNumber)
t.start()
t2.start()
t.join()
t2.join()
print(number)
print('stop')
#因为没有LOCK锁,每次执行结果都不一样
- 所以当我们多个线程争抢同一资源时需要使用LOCK锁
import threading
lock=threading.Lock()
number=0
def addNumber():
global number
for i in range(10000000):
lock.acquire() #获取锁
number+=1
lock.release() #释放锁
def downNumber():
global number
for i in range(10000000):
lock.acquire()
number-=1
lock.release()
print('start')
t=threading.Thread(target=addNumber)
t2=threading.Thread(target=downNumber)
t.start()
t2.start()
t.join()
t2.join()
print(number)
print('stop')
递归锁RLOCK
import threading
class Test:
rlock=threading.RLock()
def __init__(self):
self.number=0
def execute(self,n):
with Test.rlock:
self.number+=n
def add(self):
with Test.rlock:
self.execute(1)
def down(self):
with Test.rlock:
self.execute(-1)
def add(test):
for i in range(10000000):
test.add()
def down(test):
for i in range(10000000):
test.down()
if __name__=='__main__':
t=Test()
t1=threading.Thread(target=add,args=(t,))
t2=threading.Thread(target=down,args=(t,))
t1.start()
t2.start()
t1.join()
t2.join()
print(t.number)
'''
只有拿到锁的线程才能释放锁
同一线程可以多次拿到锁
'''
避免GIL
- 用multiprocessing替代Thread
- 多进程代替多线程
- 它使用了多进程而不是多线程。每个进程有自己的独立GIL,因此也不会出现进程之间的GIL争抢。
- 多进程的创建和销毁开销也会更大,成本高。
- 进程间无法看到对方数据,需要使用栈或者队列进行获取,编程复杂度提升
import multiprocessing
import time
def start(i):
time.sleep(1)
print(i)
print(multiprocessing.current_process().name)
print(multiprocessing.current_process().pid)
print(multiprocessing.current_process().is_alive())
if __name__=='__main__':
print('start')
p=multiprocessing.Process(target=start,args=(1,),name='pl')
p.start()
p.join()
print('stop')
'''
start
1
pl
12928
True
stop
'''
进程通信
- Python多进程之间默认是无法通信的,因为是并发执行的,所以需要借助其他数据结构。
from multiprocessing import Process,Queue
def write(q):
print('Process to write:%s'%Process.pid)
for i in range(10):
print('Put %d to queue...'%i)
q.put(i)
def read(q):
print('Process to read:%s'%Process.pid)
while True:
value=q.get()
print('Get %d from queue'%value)
if __name__=='__main__':
#父进程创建Queue,并传给各个子进程:
q=Queue()
pw=Process(target=write,args=(q,))
pr=Process(target=read,args=(q,))
#启动子进程pw,写入:
pw.start()
#启动子进程pr,读取:
pr.start()
#等待pw结束:
pw.join()
进程池
- 多个任务
import multiprocessing
def function_square(data):
result=data*data
return result
if __name__=='__main__':
inputs=list(range(100))
pool=multiprocessing.Pool(processes=4)
#map把任务交给进程池
pool_outputs=pool.map(function_square,inputs)
pool.close()
pool.join()
print('Pool:',pool_outputs)
- 单个任务
import multiprocessing
def function_square(data):
result=data*data
return result
if __name__=='__main__':
pool=multiprocessing.Pool(processes=4)
#apply把任务交给进程池
pool_outputs=pool.apply(function_square,args=(10,))
pool.close()
pool.join()
print('Pool:',pool_outputs)
线程池
- pip install threadpool
import time
import threadpool
#执行比较耗时的函数,需要开启多线程
def get_html(url):
time.sleep(3)
print(url)
#使用多线程执行telnet函数
urls=[i for i in range(10)]
pool=threadpool.ThreadPool(10)#建立线程池
#提交任务给线程池
requests=threadpool.makeRequests(get_html,urls)
#开始执行任务
for req in requests:
pool.putRequest(req)
pool.wait()