多线程、多进程和线程池编程

本文介绍了Python中的多线程、多进程编程,探讨了GIL全局解释器锁对多线程的影响,以及线程同步和线程池的概念。在多线程中,GIL会在特定情况下释放,使得线程间通信和同步成为必要。多进程编程则能利用多核优势,但进程间通信有一定复杂性。文章还提到了线程池的使用,并对比了多线程和多进程在不同场景下的优劣。
摘要由CSDN通过智能技术生成

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&#
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值