多进程的概念

进程

进程的概念

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()  # 令主进程阻塞等待池中所有进程执行完毕

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容