GIL (global interpreter lock)全局解释器锁
基于cpython的介绍:
python中的一个线程是对应于c语言中的一个线程,python在早期的时候为了简单,会在解释器上加一把非常大的锁,允许我们同一时刻只有一个线程运行在一个cpu上执行字节码。在某种程度上保证了我们的线程是安全的。它无法将多个线程映射到多个cpu上执行,使得我们无法利用多核优势。
gil使得同一时刻只有一个线程在一个cpu上执行字节码
python现在正在努力的去掉gil,像pypy是去gil的.因为cpython当中的许多第三方包使用gil的,所以短期类gil是不太可能被去掉的。
那么是不是说在一个时刻只有一个线程运行在我们的cpu上,就意味着我们在使用多线程编码的时候就是安全的呢?就不用去考虑线程之间的同步呢?实际上不是的。因为gil在某些情况下是会被释放的。gil的释放在python2和python3还有区别的。
gil的释放:
1.gil会根据执行的字节码行数以及时间片释放gil
2.gil还会在遇到io操作的时候主动释放
所以python在io操作的时候,多线程编程就变得非常重要了。
# import dis
# def add(a):
# a=a+1
# return a
# #我们可以对它进行反编译的
# print(dis.dis(add))
'''
H:\ANACONDA\python.exe I:/ainlp/pythonHight/chapter11/pythonGIL.py
3 0 LOAD_FAST 0 (a)
2 LOAD_CONST 1 (1)
4 BINARY_ADD
6 STORE_FAST 0 (a)
4 8 LOAD_FAST 0 (a)
10 RETURN_VALUE
None
'''
total=0
def add():
global total
for i in range(1000000):
total+=1
def desc():
global total
for i in range(1000000):
total-=1
import threading
thread1=threading.Thread(target=add)
thread2=threading.Thread(target=desc)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(total)
多线程编程
操作系统能够调度的最小单元是线程,在最开始的时候操作系统能够调度的最小单元实际上是进程,但是因为进程对于系统的资源消耗非常大,所以后期就演变出了线程。线程实际上是依赖于进程的。如在windows任务管理器中会看到许多的进程,如下图:
对于操作系统来说能够调度的做小单元是线程。
多线程编程:
1.通过Thread类实例化
# 对于io操作来说,多线程和多进程差别不大
import time
import threading
#1.通过Thread类实例化
def get_detail_html(url):
print("get detail html started")
time.sleep(2)
print("get detail html end")
def get_detail_url(url):
print("get detail url started")
time.sleep(4)
print("get detail url end")
if __name__=="__main__":
thread1=threading.Thread(target=get_detail_html,args={"",})
thread2 = threading.Thread(target=get_detail_url, args={"",})
#设置子进程为守护线程 当主线程退出的时候,子线程也会被关掉。
# thread1.setDaemon(True)
thread2.setDaemon(True)
start_time=time.time()
thread1.start()
thread2.start()
#那么如何使得在两个线程运行完成之后再执行主线程呢
thread1.join()
thread2.join()
'''
H:\ANACONDA\python.exe I:/ainlp/pythonHight/chapter11/python_thread.py
get detail html started
get detail url started
get detail html end
get detail url end
last time: 4.0012288093566895
在加入线程阻塞之后,主线程会等待两个子线程执行完成之后才开始执行,而两个子线程
运行所用的时间并不是顺序运行的时间,而是在一定程度的并行运行,因此是花费了线程
2所用的时间。
'''
'''
虽然在这个python脚本当中创建了2个线程,但是实际上一共有3个线程
第三个线程就是我们的python程序,即所说的主线程。
'''
print("last time: {}".format(time.time()-start_time))
2.通过继承Thread来实现多线程 适用于复杂的场景
import time
import threading
class GetDetailHtml(threading.Thread):
#重载init方法
def __init__(self,name):
super().__init__(name=name)
#重载run方法
def run(self):
print("get detail html started")
time.sleep(2)
print("get detail html end")
class GetDetailUrl(threading.Thread):
# 重载init方法
def __init__(self,name):
super().__init__(name=name)
# 重载run方法
def run(self):
print("get detail url started")
time.sleep(4)
print("get detail url end")
#通过这两个类的实例完成我们的多线程
if __name__=="__main__":
thread1=GetDetailHtml("getdetailhtml")
thread2= GetDetailUrl("getdetailurl")
# thread1.setDaemon(True)
thread2.setDaemon(True)
start_time=time.time()
thread1.start()
thread2.start()
#那么如何使得在两个线程运行完成之后再执行主线程呢
thread1.join()
thread2.join()
print("last time: {}".format(time.time() - start_time))
线程间通信
1.线程间共享变量
import time
import threading
from pythonHight.chapter11.variables import detail_url_list
from pythonHight.chapter11 import variables
#1.通过Thread类实例化
#爬取文章详情页
def get_detail_html():
detail_url_list=variables.detail_url_list
# while True:
if len(detail_url_list):
# global detail_url_list
url=detail_url_list.pop()
# for url in detail_url_list:
print("get detail html started")
time.sleep(0.5)
print("get detail html end")
#爬取文章列表页
def get_detail_url():
# global detail_url_list
detail_url_list = variables.detail_url_list
# while True:
print("get detail url started")
time.sleep(1)
for i in range(20):
detail_url_list.append("http://www.baidu.com/{id}".format(id=i))
print("get detail url end")
#1.线程间的通信方式-共享变量
'''
线程间的变量共享涉及到很多的线程安全性问题,因为gil的原因会导致
线程在处理数据的时候没有达到预期的效果,可能会造成一定的混乱。
所以并不推荐使用共享变量进行通信,除非大家对锁比较了解
'''
#2.
if __name__=="__main__":
thread_detail_url=threading.Thread(target=get_detail_url,args=())
'''
在真正爬取url列表页面的时候是很快的,而爬取每个url里的详情速度是很慢的
如果这两个爬取方式只是分别之构建一个线程,则没有做到高并发,那么就可以
考虑构建多个爬取详情页的线程来增加并发,提高运行效率
'''
'''
直观的做法:
thread_detail_html1 = threading.Thread(target=get_detail_html, args&#