进程
进程的概念
python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。
进程的概念:
进程是程序的一次执行过程, 正在进行的一个过程或者说一个任务,而负责执行任务的则是CPU.
进程的生命周期:
当操作系统要完成某个任务时,它会创建一个进程。当进程完成任务之后,系统就会撤销这个进程,收回它所占用的资源。从创建到撤销的时间段就是进程的生命期
进程之间存在并发性:
在一个系统中,同时会存在多个进程。他们轮流占用CPU和各种资源
并行与并发的区别:
无论是并行还是并发,在用户看来都是同时运行的,不管是进程还是线程,都只是一个任务而已,
真正干活的是CPU,CPU来做这些任务,而一个cpu(单核)同一时刻只能执行一个任务。
并行:多个任务同时运行,只有具备多个cpu才能实现并行,含有几个cpu,也就意味着在同一时刻可以执行几个任务。
并发:是伪并行,即看起来是同时运行的,实际上是单个CPU在多道程序之间来回的进行切换。
同步与异步的概念:
同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去。
异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进行处理,这样可以提高执行的效率。
比如:打电话的过程就是同步通信,发短信时就是异步通信。
多线程和多进程的关系:
对于计算密集型应用,应该使用多进程;
对于IO密集型应用,应该使用多线程。线程的创建比进程的创建开销小的多。
创建进程
使用multiprocessing.Process
import multiprocessing
import time
def func(arg):
pname = multiprocessing.current_process().name
pid = multiprocessing.current_process().pid
print("当前进程ID=%d,name=%s" % (pid, pname))
for i in range(5):
print(arg)
time.sleep(1)
if __name__ == "__main__":
p = multiprocessing.Process(target=func, args=("hello",))
# p.daemon = True # 设为【守护进程】(随主进程的结束而结束)
p.start()
while True:
print("子进程是否活着?", p.is_alive())
time.sleep(1)
print("main over")
通过继承Process实现自定义进程
import multiprocessing
import os
# 通过继承Process实现自定义进程
class MyProcess(multiprocessing.Process):
def __init__(self, name, url):
super().__init__()
self.name = name
self.url = url # 自定义属性
# 重写run
def run(self):
pid = os.getpid()
ppid = os.getppid()
pname = multiprocessing.current_process().name
print("当前进程name:", pname)
print("当前进程id:", pid)
print("当前进程的父进程id:", ppid)
if __name__ == '__main__':
# 创建3个进程
MyProcess("小分队1", "").start()
MyProcess("小分队2", "").start()
MyProcess("小分队3", "").start()
print("主进程ID:", multiprocessing.current_process().pid)
# CPU核数
coreCount = multiprocessing.cpu_count()
print("我的CPU是%d核的" % coreCount)
# 获取当前活动的进程列表
print(multiprocessing.active_children())
同步异步和进程锁
import multiprocessing
import random
import time
def fn():
name = multiprocessing.current_process().name
print("开始执行进程:", name)
time.sleep(random.randint(1, 4))
print("执行结束:", name)
# 多进程
# 异步执行进程
def processAsync():
p1 = multiprocessing.Process(target=fn, name="小分队1")
p2 = multiprocessing.Process(target=fn, name="小分队2")
p1.start()
p2.start()
# 同步执行
def processSync():
p1 = multiprocessing.Process(target=fn, name="小分队1")
p2 = multiprocessing.Process(target=fn, name="小分队2")
p1.start()
p1.join()
p2.start()
p2.join()
# 加锁
def processLock():
# 进程锁
lock = multiprocessing.Lock()
p1 = multiprocessing.Process(target=fn2, name="小分队1", args=(lock,))
p2 = multiprocessing.Process(target=fn2, name="小分队2", args=(lock,))
p1.start()
p2.start()
def fn2(lock):
name = multiprocessing.current_process().name
print("开始执行进程:", name)
# 加锁
# 方式一
# if lock.acquire():
# print("正在工作...")
# time.sleep(random.randint(1, 4))
# lock.release()
# 方式二
with lock:
print("%s:正在工作..." % name)
time.sleep(random.randint(1, 4))
print("%s:执行结束:"% name)
if __name__ == '__main__':
# processAsync() # 异步执行
# processSync() # 同步执行
processLock() # 加进程锁
使用Semaphore控制进程的最大并发
import multiprocessing
import time
def fn(sem):
with sem:
name = multiprocessing.current_process().name
print("子线程开始:", name)
time.sleep(3)
print("子线程结束:", name)
if __name__ == '__main__':
sem = multiprocessing.Semaphore(3)
for i in range(8):
multiprocessing.Process(target=fn, name="小分队%d"%i, args=(sem, )).start()
练习: 多进程抓取链家 https://sz.lianjia.com/ershoufang/rs/
练习: 多进程+多协程抓取链家 https://sz.lianjia.com/ershoufang/rs/
练习: 多线程分页抓取斗鱼妹子 https://www.douyu.com/gapi/rkc/directory/2_201/4
练习: 多进程分页抓取斗鱼妹子 https://www.douyu.com/gapi/rkc/directory/2_201/4
matplotlib
matplotlib介绍和简单使用
Matplotlib是什么
Matplotlib 是一个 Python 的 2D绘图库
通过 Matplotlib,开发者可以仅需要几行代码,便可以生成绘图,直方图,功率谱,条形图,错误图,散点图等。
Matplotlib的简单使用
import matplotlib
from matplotlib import pyplot as plt
# 显示中文
# 配置字体
matplotlib.rcParams['font.sans-serif'] = ['simhei']
matplotlib.rcParams['font.family'] = 'sans-serif'
# 画线
# plt.plot([1,2], [3,5])
# plt.plot([1,3,7], [2,5,8])
# plt.plot([1,3,7], [2,5,8], '--') # 虚线
# xy轴文字
plt.xlabel('x轴')
plt.ylabel('y轴')
# 参数x:x轴位置
# 参数height:高度
# 参数width:默认0.8
plt.bar([1], [123], label='bj')
plt.bar([3], [245], label='sz')
plt.legend() # 绘制
# plt.show() # 显示
plt.savefig('line') # 保存图片
练习:爬取51job不同职位的岗位数量,并用折线图和条形图表示出来
url = "https://search.51job.com/list/000000,000000,0000,00,9,99,%s,2,1.html" % job
扩展
线程池
import threading
import threadpool
import time
import random
# ================================================================= #
def fn(who):
tname = threading.current_thread().getName()
print("%s开始%s..." % (tname, who))
time.sleep(random.randint(1, 5))
print("-----%s,%s-----" % (tname, who))
# ================================================================= #
# 请求执行结束回调
# request=已完成的请求
# result=任务的返回值
def cb(request, result):
print("cb", request, result)
if __name__ == '__main__':
# 创建一个最大并发为4的线程池(4个线程)
pool = threadpool.ThreadPool(4)
argsList = ["张三丰", "赵四", "王五", "六爷", "洪七公", "朱重八"]
# 允许回调
requests = threadpool.makeRequests(fn, argsList, callback=cb)
for req in requests:
pool.putRequest(req)
# 阻塞等待全部请求返回(线程池创建的并发默认为【守护线程】)
pool.wait()
print("Over")
进程池
import multiprocessing
import random
import time
def fn1(arg, name):
print("正在执行任务1: {}...".format(arg))
time.sleep(random.randint(1, 5))
print("进程%d完毕!" % (name))
def fn2(arg, name):
print("正在执行任务2: {}...".format(arg))
time.sleep(random.randint(1, 5))
print("进程%d完毕!" % (name))
# 回调函数
def onback(result):
print("得到结果{}".format(result))
if __name__ == "__main__":
# 待并发执行的函数列表
funclist = [fn1, fn2, fn1, fn2]
# 创建一个3并发的进程池
pool = multiprocessing.Pool(3)
# 遍历函数列表,将每一个函数丢入进程池中
for i in range(len(funclist)):
# 同步执行
# pool.apply(func=funclist[i], args=("hello", i))
# 异步执行
pool.apply_async(func=funclist[i], args=("hello", i), callback=onback)
pool.close() # 关闭进程池,不再接收新的进程
pool.join() # 令主进程阻塞等待池中所有进程执行完毕