文章目录

  • 1.Python 创建多线程的方法
  • 2.Python实现生产者消费者爬虫
  • 3.Python线程安全
  • 4.线程池
  • 5.使用线程池优化Web服务器

1.Python 创建多线程的方法

  • 步骤:
1、准备一个函数
def my_func(a, b):do_craw(a,b)2、怎样创建一个线程
import threading
t = threading.Thread(target=my_func, args=(100, 200)3、启动线程
t.start()4、等待结束
t.join()
  • eg:blog_spider.py
import requests
from bs4 import BeautifulSoupurls = [f"https://www.cnblogs.com/sitehome/p/{page}"for page in range(1, 50 + 1)
]def craw(url):#print("craw url: ", url)r = requests.get(url)##len(r.text)表示该url网页的长度,r.text表示网页的内容# print(url, len(r.text))return r.textdef parse(html):# class="post-item-title"soup = BeautifulSoup(html, "html.parser")links = soup.find_all("a", class_="post-item-title")##解析出链接:(link["href"]##解析出来标题:link.get_text()return [(link["href"], link.get_text()) for link in links]if __name__ == "__main__":for result in parse(craw(urls[2])):print(result)
  • 1.multi_thread_craw.py
import blog_spider
import threading
import timedef single_thread():print("single_thread begin")for url in blog_spider.urls:blog_spider.craw(url)print("single_thread end")def multi_thread():print("multi_thread begin")threads = []##开启了50个线程,因为blog_spider.urls有50个for url in blog_spider.urls:threads.append(##(url,)表示其元组,(url)表示的是字符串threading.Thread(target=blog_spider.craw, args=(url,)))for thread in threads:thread.start()for thread in threads:thread.join()print("multi_thread end")if __name__ == "__main__":start = time.time()single_thread()end = time.time()print("single thread cost:", end - start, "seconds")start = time.time()multi_thread()end = time.time()print("multi thread cost:", end - start, "seconds")

2.Python实现生产者消费者爬虫

  • eg:02. producer_consumer_spider.py
import queue
import blog_spider
import time
import random
import threading##输入队列url_queue,输出队列:html_queue
##queue.Queue仅仅做标明类型作用,不用也行
def do_craw(url_queue: queue.Queue, html_queue: queue.Queue):while True:url = url_queue.get()html = blog_spider.craw(url)html_queue.put(html)##打印当前线程的名字:threading.current_thread().nameprint(threading.current_thread().name, f"craw {url}","url_queue.size=", url_queue.qsize())##随机睡眠1-2stime.sleep(random.randint(1, 2))def do_parse(html_queue: queue.Queue, fout):while True:html = html_queue.get()results = blog_spider.parse(html)for result in results:fout.write(str(result) + "\n")print(threading.current_thread().name, f"results.size", len(results),"html_queue.size=", html_queue.qsize())time.sleep(random.randint(1, 2))if __name__ == "__main__":url_queue = queue.Queue()html_queue = queue.Queue()##主线程将数据扔到生产者for url in blog_spider.urls:url_queue.put(url)##3个生产者线程,生产者产生中间的数据仍到html_queuefor idx in range(3):##name=f"craw{idx}"表示线程名字t = threading.Thread(target=do_craw, args=(url_queue, html_queue),name=f"craw{idx}")t.start()##2个消费者线程对html_queue进行处理fout = open("02.data.txt", "w")for idx in range(2):t = threading.Thread(target=do_parse, args=(html_queue, fout),name=f"parse{idx}")t.start()

爬取标题

  • 测试:

3.Python线程安全

  • 线程安全指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成

  • 由于线程的执行随时会发生切换,就造成了不可预料的结果,出现线程不安全

  • Lock 用于解决线程安全问题

用法1:try-finally模式
import threadinglock = threading.Lock()lock.acquire()
try:# do something
finally:lock.release()用法2:with 模式
import threadinglock = threading.Lock()with lock:# do something
  • eg:
import threading
import timelock = threading.Lock()class Account:def __init__(self, balance):self.balance = balancedef draw(account, amount):with lock:if account.balance >= amount:##sleep一定会导致线程阻塞,进行线程切换time.sleep(0.1)print(threading.current_thread().name,"取钱成功")account.balance -= amountprint(threading.current_thread().name,"余额", account.balance)else:print(threading.current_thread().name,"取钱失败,余额不足")if __name__ == "__main__":account = Account(1000)ta = threading.Thread(name="ta", target=draw, args=(account, 800))tb = threading.Thread(name="tb", target=draw, args=(account, 800))ta.start()tb.start()
  • 测试:

4.线程池

  • 线程:新建线程系统需要分配资源、终止线程系统需要回收资源
    如果可以重用线程,则可以减去新建/终止的开销

  • 线程池:线程池(可重用线程)+任务队列实现了线程池的功能
    提前建好的线程,线程可以重复使用,好处是:减少了新建/终止线程的开销以及线程切换的开销。
    新任务到来时先放到任务队列中,线程池中的线程会挨个从任务队列中取出任务依次执行,没有任务了就回到线程池等待下一个任务的到来。

  • 使用线程池的好处
    (1)提升性能:因为减去了大量新建、终止线程的开销,重用了线程资源;
    (2)适用场景:适合处理突发性大量请求或需要大量线程完成任务、但实际任务处理时间较短
    (3)防御功能:能有效避免系统因为创建线程过多,而导致系统负荷过大相应变慢等问题
    (4)代码优势:使用线程池的语法比自己新建线程执行线程更加简洁

from concurrent.futures import ThreadPoolExecutor, as_completed
用法1:map函数,很简单
注意map的结果和入参是顺序对应的
with ThreadPoolExecutor() as pool:##urls是参数列表results = pool.map(craw, urls)for result in results:print(result)用法2:future模式,更强大
注意如果用as_completed顺序是不定的with ThreadPoolExecutor() as pool:futures = [ pool.submit(craw, url)for url in urls ]##按照顺序进行打印for future in futures:print(future.result())##谁先执行完,就打印谁for future in as_completed(futures):print(future.result())
  • eg:04. thread_pool.py

import concurrent.futures
import blog_spider# craw
with concurrent.futures.ThreadPoolExecutor() as pool:htmls = pool.map(blog_spider.craw, blog_spider.urls)##zip(blog_spider.urls, htmls)将每个url和html对应起来htmls = list(zip(blog_spider.urls, htmls))for url, html in htmls:print(url, len(html))print("craw over")# parse
with concurrent.futures.ThreadPoolExecutor() as pool:futures = {}for url, html in htmls:##参数是单个htmlfuture = pool.submit(blog_spider.parse, html)futures[future] = url##输出方法1#for future, url in futures.items():#    print(url, future.result())##输出方法2for future in concurrent.futures.as_completed(futures):url = futures[future]print(url, future.result())
  • 测试:
    按照顺序获取

    输出方法1的结果,按照顺序解析

    输出方法2的结果,不是按照顺序解析

5.使用线程池优化Web服务器

  • Web服务的架构以及特点
    (1)Web服务对响应时间要求非常高,比如要求200MS返回
    (2)Web服务有大量的依赖IO操作的调用,比如磁盘文件、数据库、远程API
    (3)Web服务经常需要处理几万人、几百万人的同时请求
  • 使用线程池ThreadPoolExecutor加速
    使用线程池ThreadPoolExecutor的好处:
    (1)方便的将磁盘文件、数据库、远程API的IO调用并发执行
    (2)线程池的线程数目不会无限创建(导致系统挂掉),具有防御功能
  • eg:05. flask_thread_pool.py
import flask
import json
import time
from concurrent.futures import ThreadPoolExecutor##起个名字
app = flask.Flask(__name__)##初始化一个pool对象
pool = ThreadPoolExecutor()def read_file():##100毫秒,sleep模拟IO操作time.sleep(0.1)return "file result"def read_db():time.sleep(0.2)return "db result"def read_api():time.sleep(0.3)return "api result"@app.route("/")
def index():# 不用pool##模拟读取web的3个三个操作# result_file = read_file# result_db = read_db# result_api = read_api# return json.dumps({#     "result_file": result_file,#     "result_db": result_db,#     "result_api": result_api,# })# 使用pool##模拟读取web的3个三个操作result_file = pool.submit(read_file)result_db = pool.submit(read_db)result_api = pool.submit(read_api)return json.dumps({"result_file": result_file.result(),"result_db": result_db.result(),"result_api": result_api.result(),})if __name__ == "__main__":##启动flask方法app.run()
  • 测试:
    点开就可以看到浏览器访问的结果

    各方法访问的时间的测试,win可以使用postman。下面的结果是不用pool

    使用pool加速后的结果,运行的最长时间是300ms,另外2个是在300ms运行期间完成的,由原来三者方法加和时间,变成了耗费最长方法的时间

  • 参考:链接

(P4-P8)多线程,线程池相关推荐

  1. 多线程线程池的实现java_如何在Java中实现线程池

    多线程线程池的实现java 线程是独立程序的执行路径. 在java中,每个线程都扩展java.lang.Thread类或实现java.lang.Runnable. 多线程是指在一个任务中同时执行两个或 ...

  2. 多线程线程池的基本创建,使用方法

    import java.util.concurrent.*;/*** 多线程线程池的基本创建,使用方法** @author silence*/ public class Silence {public ...

  3. 【Java从入门到头秃专栏 6】语法篇(五) :多线程 线程池 可见、原子性 并发包 Lambda表达式

    目录 1 多线程 1.1 基本概念 1.2 创建线程的三种方式 1.4 解决线程安全问题的三种方法 1.5 线程通信 1.6 线程状态 2 线程池 2.1线程池的概念 2.2 创建并提交任务 3 可见 ...

  4. 一心多用多线程-线程池ThreadPoolExecutor-看这篇就够了

    许久之前理解了java线程池ThreadPoolExecutor,今天来做一个总结,根据java api加上自己的理解,让我们能更透彻的理解java线程池 首先先写一下线程池的概念: 线程池:线程池是 ...

  5. Java多线程 线程池Executor框架

    目录 一.说明 二.理解 Executor ExecutorService Executors 三.实现 1. newSingleThreadExecutor 2. newFixedThreadPoo ...

  6. 多线程---线程池的使用

    线程池的使用 一.Executors - 线程池的工厂 二.JDK自带的一些线程池 1. SingleThreadExecutor 2. FixThreadPool 3. CachedThreadPo ...

  7. Java多线程——线程池的饥饿现象

    概述 定长线程池的使用过程中会存在饥饿现象,也就是当多线程情况下,当池中所有线程都被占用后,被占用的线程又需要空闲线程去进行下一步的操作,此时又获取不到池中空闲的线程,此时就出现了饥饿现象. 示例 p ...

  8. Java多线程- 线程池的基本使用和执行流程分析 - ThreadPoolExecutor

    线程池的实现原理 池化技术 一说到线程池自然就会想到池化技术. 其实所谓池化技术,就是把一些能够复用的东西放到池中,避免重复创建.销毁的开销,从而极大提高性能. 常见池化技术的例如: 线程池 内存池 ...

  9. C# 多线程 线程池(ThreadPool) 2 如何控制线程池?

    线程池启动了,但是没有方法去控制线程池,如果子线程出现了问题,难道线程池就死了吗? 我们可以设置线程池的线程数量,进行加入任务,线程池会自动分配并且合理的执行,但是控制不了又有啥意思呢. 线程池里线程 ...

  10. 多线程—线程池Executor框架及四种常用线程池

    池化技术应用:线程池.数据库连接池.http连接池等等. 池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率. 使用线程池的好处: 降低资源消耗:通过重复利用已创建的线程降低线程创建和 ...

最新文章

  1. 深度置信网络_人工智能深度学习之父Hinton深度置信网络北大最新演讲(含PPT)...
  2. 《Adobe Flash Professional CC经典教程》——1.13 查找关于使用Flash的资源
  3. Effective Java:创建和销毁对象
  4. datalength,求字符串的字节数
  5. 记录element-ui级联选择器,二级三级列表无法显示的解决办法
  6. SharePoint WebPart:扩展SharePoint 2007中图片展示功能
  7. 2021牛客暑期多校训练营6
  8. Python datetime isocalendar()方法与示例
  9. IOS – OpenGL ES 调节图像阴影 GPUImageHighlightShadowFilter
  10. 底层软件工程师的一次冒险经历
  11. 是什么意思网络语_网络语“随薪锁欲”是什么意思?
  12. 十大经典算法总结(JavaScript描述)
  13. php ftp上传文件 源码,PHP FTP上传文件
  14. 海康VisionMaster算法平台介绍
  15. 李炎恢python_前端教程:李炎恢jQuery视频教程 百度网盘
  16. windows和Linux下西部数据C1门解决方法
  17. unity3d:激活码系统(根据PC机器码,给对应激活码完成软件注册)
  18. 关于VBV-------Video Buffering Verifier
  19. Flutter 编译失败shared_preferences_macos
  20. 织梦如何添html5播放器,织梦ckplayer视频播放器插件的介绍与使用

热门文章

  1. 游戏项目和开源项目调研
  2. JS sort函数(按照首字母字符排序)
  3. 如何在年薪10万的上班族让自己第二年达到20万以上?
  4. 网吧游戏下载期,内置超10000G游戏!
  5. POJ - 3179 Corral the Cows【离散化】【前缀和】
  6. 将汉字转化为拼音,正则表达式和得到汉字的Unicode编码
  7. 深入理解 RecyclerView 的绘制流程和滑动原理,直面春招
  8. php上传常见文件类型和$_FILES[file][type]
  9. VS模板 孤狼优化版v1.0
  10. Ros:people包下子包leg_detector及其相关包笔记