python-并发编程(1)

python并发编程1

  • 全局解释器锁GIL
    • python速度慢的原因
    • GIL是什么
    • 为什么有GIL
    • 怎么避免GIL带来的限制
  • python创建多线程
    • 多线程爬虫任务
  • 生产者消费者的多线程爬虫
    • 多组件的pipeline技术架构
    • 生产者消费者爬虫的架构

  • 多线程:threading,利用cp和IO口可以同时执行的原理,让cpu在等待IO的时候去完成其他任务

  • 多进程:multiprocessing,利用多核CPU的能力,实现并行

  • 异步IO,asyncio,在单线程利用CPU和IO同时执行的原理,是按函数异步执行

  • 使用Lock对资源枷锁,防止冲突访问

  • 使用Queue实现不同线程/进程之间的数据通信,实现生产者-消费者模式

  • 使用线程池pool和进程池,简化线程/进程的任务提交,等待结束获取结果

  • 使用subprocess启动外部程序的进程,并进行输入输出交互

全局解释器锁GIL

python速度慢的原因

python是动态类型语言,边解释边执行
python用到了GIL,不能利用多核CPU并行的能力

GIL是什么

全局解释器锁,他使得任何时刻仅有一个线程在执行,即使在多核处理器上,在同一时刻只允许一个线程
python-并发编程(1)_第1张图片

为什么有GIL

引入GIL是为了解决多线程之间数据完整性和状态同步的问题,
python-并发编程(1)_第2张图片
这个时候线程b已经把对象obj释放掉了。但是一旦切换到线程A,他检测到obj计数为0,就又释放了一次,这个时候一旦obj正在被用,就可能破坏别人的内存结构

怎么避免GIL带来的限制

使用multiprocessing多进程

python创建多线程

多线程爬虫任务

爬虫任务被线程加速,实例如下

import threading 
import requests
import time

urls=[
      f"https://www.cnblogs.com/p{page}"
      for page in range(1,50+1)
      ]

def craw(url):
    r=requests.get(url)
    print(url,len(r.text))



def single_thread():
    print("single_thread shart")
    for url in urls:
        craw(url)
    print("singal_thread end")
        

def muti_thread():
    print("muti_thread start")
    threads=[] 
    for url in urls:
        threads.append(threading.Thread(target=craw,args=(url,)))//关键语句
        
    for thread in threads:
        thread.start()
        
    # for thread in threads:
    #     thread.join()
        
    print("muti_thread end")
        
    
if __name__=="__main__":
    start_time=time.time()
    single_thread()
    end_time=time.time()
    print(f"use time:{end_time-start_time}")
    
    
    start_time=time.time()
    muti_thread()
    end_time=time.time()
    print(f"use time:{end_time-start_time}")
    

use time:9.698142290115356
use time:0.46600961685180664

生产者消费者的多线程爬虫

多组件的pipeline技术架构

python-并发编程(1)_第3张图片

生产者消费者爬虫的架构

python-并发编程(1)_第4张图片

python-并发编程(1)_第5张图片

设置了三个成产者两个消费者,简单来说就是生产者生产,放进队列,安排消费者消费。

# -*- coding: utf-8 -*-
"""
Created on Mon May  3 20:10:32 2021

@author: hellohaojun
"""

import requests
from bs4 import BeautifulSoup
import   queue
import time
import random
import threading

urls=[
      f"https://www.cnblogs.com/#p{page}"
      for page in range(1,50+1)
      ]

def craw(url):
    r=requests.get(url)
    return r.text

def parse(html):
    soup=BeautifulSoup(html,"html.parser")
    links=soup.find_all("a",class_="post_item-title")
    return[(link["href"],link.get_text())for link in links]

        
        
def do_craw(url_queue:queue.Queue,html_queue:queue.Queue):
    while True:
        url=url_queue.get()
        html=craw(url)
        html_queue.put(html)
        print(threading.currentThread().name,f"craw{url}","url_quque.size=",url_queue.qsize())
        time.sleep(random.randint(1, 2))
        
def do_parse(html_queue:queue.Queue,fout):
    while True:
        html=html_queue.get()
        results=parse(html) 
        for result in  results:
            fout.write(str(result)+"\n")
        print(threading.currentThread().name,f"result.size",len(results),
              "html_quque.size=",html_queue.qsize())
        time.sleep(random.randint(1, 2))
        
        
if __name__=="__main__":
    url_queue=queue.Queue()
    html_queue=queue.Queue()
    for url in urls:
        url_queue.put(url)
        
    for idx in range(3):
        t=threading.Thread(target=do_craw,args=(url_queue,html_queue),
                           name=f"craw{idx}")
        t.start()
    
    fout=open("html.txt","w")
    for idx in range(2):
        t=threading.Thread(target=do_parse,args=(html_queue,fout),
                           name=f"parse{idx}")
        t.start()
    
      

python-并发编程(1)_第6张图片

你可能感兴趣的:(python入门到实践,python,python,多线程,多进程,queue)