python学习笔记(GIL全局解释器锁、线程池)

本文探讨了Python中的全局解释器锁(GIL)如何影响多线程性能,并介绍了线程同步的重要性,通过实例展示了线程锁的使用。此外,还讲解了生产者消费者模型在多线程中的应用,以及线程池在处理任务调度中的优势。内容涵盖队列、线程锁、线程池的使用以及下载器和地理IP位置的多线程实现。
摘要由CSDN通过智能技术生成

GIL

python使用多线程, 一定运行速度快么? 为什么?

  • GIL(global interpreter lock)
  • python解释器中任意时刻都只有一个线程在执行;
  • GIL执行过程:
    • 1). 设置一个GIL;
    • 2). 切换线程去准备执行任务(Runnale就绪状态);
    • 3). 运行;
    • 4). 可能出现的状态:
      - 线程任务执行结束;
      - time.sleep()
      - 需要获取其他的信息才能继续执行(eg: 读取文件, 需要从网络下载html网页)
    • 5). 将线程设置为睡眠状态;
    • 5). 解GIL的锁;

多线程的应用场景: I/O密集型(input, output) — 爬虫
不建议使用多线程的场景: 计算密集型(cpu一直占用)


import threading
from day20.mytimeit import timeit


def job(li):
    return  sum(li)


@timeit
def use_thread():
    li = range(1, 10001)
    # create 5 threads
    threads = []
    for i in range(5):
        t = threading.Thread(target=job, args=(li, ))
        t.start()
        threads.append(t)
    [thread.join() for thread in  threads]

@timeit
def use_no_thread():
    li = range(1, 10001)
    for i in range(5):
        job(li)

if __name__ == "__main__":
    use_thread()
    use_no_thread()

在这里插入图片描述

队列与线程

1). 理论上多线程执行任务是不能获取返回结果的, 因此需要一个容器来存储产生的数据;
2). 容器该如何选择? list(栈, 队列), tuple(元组是不可变的, 不可使用),
set(集合默认会去重, 所以不选择), dict
选择队列类型存储(FIFO===first input first output)

import threading
from day20.mytimeit import timeit
from queue import Queue


def job(li, queue):
    queue.put(sum(li))   # 将任务的执行结果存储到队列中;
@timeit
def use_thread():
    # 实例化一个队列, 用来存储每个线程执行的结果
    q = Queue()
    # q.get()  -- 出队
    # q.put(value)  -- 入队

    lis = [range(5), range(2,10), range(1000, 20000), range(3000, 10000)]
    # create 5 threads
    threads = []
    for li in lis:
        t = threading.Thread(target=job, args=(li, q))
        t.start()
        threads.append(t)
    [thread.join() for thread in threads]
    # 从队列中拿出所有线程执行的结果;
    results  = [q.get() for li in lis]
    print(results)

if __name__ == "__main__":
    use_thread()

在这里插入图片描述

生产者消费者模型、类的继承实现

需求1: 给定200个ip地址, 可能开放端口为80, 443, 7001, 7002, 8000, 8080,

9000(flask), 9001
以http://ip:port形式访问页面以判断是否正常访问.

     1). 构建所有的url地址;===存储到一个数据结构中
     2). 依次判断url址是否可以成功访问

实现多线程:
1). 实例化对象threading.Thread;
2). 自定义类, 继承threading.Thread, 重写run方法(存储任务程序);

什么是生产者-消费者模型?

某个模块专门负责身缠数据, 可以认为是工厂;
另外一个模块负责对生产的数据进行处理的, 可以认为是消费者.
在生产者和消费者之间加个缓冲区(队列queue实现), 可以认为是商店.

生产者 -----》缓冲区 -----》 消费者

优点:
1). 解耦:生产者和消费者的依赖关系减少;
2). 支持并发;是两个独立的个体, 可并发执行;
def create_data():
    """创建测试数据,  文件中生成200个IP"""
    with open('doc/ips.txt', 'w') as f:
        for i in range(200):
            f.write('172.25.254.%s\n' % (i + 1))
        print("测试数据创建完成!")

import time
import threading
from queue import Queue
from urllib.request import urlopen

class Producer(threading.Thread):
    def __init__(self, queue):
        super(Producer, self).__init__()
        self.q = queue


    def run(self):
        """生产测试需要的url地址http://ip:port"""
        ports = [80, 443, 7001, 7002, 8000, 8080, 9000, 9001]
        with open('doc/ips.txt') as f:
            for line in f:
                ip = line.strip()
                for port in ports:
                    url = "http://%s:%s" %(ip, port)
                    time.sleep(1)
                    self.q.put(url)
                    print("生产者生产url:%s" %(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值