day19.5-多线程和多进程

多线程基础

一个进程中默认有且只有一个线程,叫主线程
默认情况所有代码都是在主线程中执行

进程中主线程以外的线程都叫子线程

1.怎么让进程拥有子线程
在进程中创建threading模块中的Thread类的对象或Thread类的子类对象

2.程序(进程)的结束
只有进程中有的线程都结束了进程才会结束

from threading import *
from datetime import datetime
import time


def download(film_name: str):
    print('%s开始下载:%s' % (film_name, datetime.now()))
    time.sleep(5)
    print('%s下载结束:%s' % (film_name, datetime.now()))


# download('《三体一:地球往事》')
# download('《三体二:黑暗森林》')
# download('《三体三:死神永生》')
1.创建线程对象 - 子线程
"""
线程对象 = Thread(target=需要在子线程中调用的函数,args=元组)

说明:target - 需要在子线程中执行的任务
    args - target对应的函数在调用的时候传的参数
"""

t1 = Thread(target=download, args=('《三体一:地球往事》',))
t2 = Thread(target=download, args=('《三体二:黑暗森林》',))
t3 = Thread(target=download, args=('《三体三:死神永生》',))

# 2.让子线程执行子线程中的任务
# 线程对象.start() - 在子线程中调用target对应的函数,并且将args中的元素作为参数
# 线程开始之后只有自然死亡或者异常两种情况能终止

t1.start()
t2.start()
t3.start()

from threading import *
from datetime import datetime
import time


class DownloadThread(Thread):
    def __init__(self, film_name):
        super().__init__()
        self.film_name = film_name

    # 这个run方法是会在子线程中自动调用的方法,要求除了self以外不能有其他参数
    def run(self) -> None:
        print('%s开始下载%s' % (self.film_name, datetime.now()))
        time.sleep(5)
        print('%s结束下载%s' % (self.film_name, datetime.now()))


# 在子线程中执行下载操作
t1 = DownloadThread('电影1')
t2 = DownloadThread('电影2')
t3 = DownloadThread('电影3')

t1.start()
t2.start()
t3.start()

多线程

Thread Process
线程和进程对⽐
使⽤线程的⽅式不能很好的使⽤多核cpu的能⼒

from random import randint
from threading import Thread

results = []


def compute():
    # 计算100万个1-100之间的随机数的和
    total = sum([randint(1, 100) for i in range(1000000)])
    results.append(total)


def main():
    # 生成8个线程对象
    threads = [Thread(target=compute) for i in range(20)]

    # 启动多线程
    for thread in threads:
        thread.start()

    # 主线程等待所有子线程执行完毕
    for thread in threads:
        thread.join()

    # 多线程是共享进程里面的内存空间
    print(results)


if __name__ == '__main__':
    main()

进程池

from multiprocessing import Pool
from random import randint


def compute(n):
    # 计算100万个1-100之间的随机数的和
    total = sum([randint(1, 100) for i in range(1000000)])
    return total


def main():
    # 构建了一个进程池,进程数量是固定得
    pool = Pool(processes=8)
    result = pool.map(compute, range(8))
    print(result)


if __name__ == '__main__':
    main()

多进程

Python中的多线程⽆法利⽤多核优势 , 所以如果我们想要充分地使⽤多核CPU的资源 , 那么就只能靠多
进程了
multiprocessing模块中提供了Process , Queue , Pipe , Lock , RLock , Event , Condition等组件 , 与
threading模块有很多相似之处

from multiprocessing import Process
from random import randint

results = []


def compute():
    # 计算100万个1-100之间的随机数的和
    total = sum([randint(1, 100) for i in range(1000000)])
    results.append(total)


def main():
    processes = [Process(target=compute) for _ in range(8)]
    for process in processes:
        process.start()

    for process in processes:
        process.join()

    # 每个进程有自己独立的内存空间,进程间不共享内存
    # 如果要共享的话,要做多进程通信
    print(results)


if __name__ == '__main__':
    main()

进程队列/Queue

不同进程间内存是不共享的,要想实现两个进程间的数据交换。进程间通信有两种主要形式 , 队列和管

from multiprocessing import Process, Queue
import time


def f(q):
    # 子进程往队列里面放东西
    print('进入子进程')
    time.sleep(3)
    q.put('two')
    print('出子进程')


def main():
    # 实现一个支持进程通信的队列
    q = Queue()

    # 父进程往队列里放东西
    q.put('one')

    # 起一个子进程,让它执行f方法,并且把队列给他,args就是队列参数
    process = Process(target=f, args=(q,))
    process.start()
    process.join()

    # 非阻塞从队列里取东西,不会等待,如果没有东西会报错
    print(q.get_nowait())
    print(q.get_nowait())
    # 阻塞等待,一直等到有东西才返回
    # print(q.get())
    # print(q.get())


if __name__ == '__main__':
    main()
from multiprocessing import Process, Queue
from random import randint


def compute(q):
    # 计算100万个1-100之间的随机数的和
    total = sum([randint(1, 100) for i in range(1000000)])
    q.put(total)


def main():
    q = Queue()
    processes = [Process(target=compute, args=(q,)) for i in range(8)]

    for process in processes:
        process.start()

    for process in processes:
        process.join()

    while not q.empty():
        print(q.get_nowait(), end=' ')


if __name__ == '__main__':
    main()

管道/Pipe

The Pipe() function returns a pair of connection objects connected by a pipe which by default is
duplex (two-way).

from multiprocessing import Process, Pipe


def f(c_pipe):
    print(c_pipe.recv())
    print(c_pipe.recv())
    c_pipe.send('儿子吐水1')


def main():
    p_pipe, c_pipe = Pipe()
    process = Process(target=f, args=(c_pipe,))
    p_pipe.send('吐水1次')
    p_pipe.send('吐水2次')

    process.start()
    print(p_pipe.recv())
    print(p_pipe.recv())


if __name__ == '__main__':
    main()

Manager

进程之间是相互独⽴的 ,Queue和pipe只是实现了数据交互,并没实现数据共享,Manager可以实现进
程间数据共享
Manager还⽀持进程中的很多操作 , ⽐如Condition , Lock , Namespace , Queue , RLock , Semaphore

from multiprocessing import Process, Manager
from random import randint
import os


def compute(result_dict):
    # 计算100万个1-100之间的随机数的和
    total = sum([randint(1, 100) for i in range(1000000)])
    result_dict[os.getpid()] = total


def main():
    # 创建一个Manager上下文环境
    with Manager() as manager:
        result_dict = manager.dict()
        processes = [Process(target=compute, args=(result_dict,)) for i in range(8)]

        for process in processes:
            process.start()
        for process in processes:
            process.join()

        print(result_dict)


if __name__ == '__main__':
    main()

进程锁

from multiprocessing import Process, Lock


def f(i):
    # 获取锁
    # lock.acquire()
    print(i)
    # lock.release()


def main():
    lock = Lock()
    for i in range(100):
        # lock.acquire()
        process = Process(target=f, args=(i,))
        process.start()
        # lock.release()
        process.join()


if __name__ == '__main__':
    main()

进程池apply/apply_async

进程池内部维护⼀个进程序列,当使⽤时,则去进程池中获取⼀个进程,如果进程池序列中没有可供使
⽤的进程,那么程序就会等待,直到进程池中有可⽤进程为⽌。
进程池中有以下⼏个主要⽅法:

  1. apply:从进程池⾥取⼀个进程并执⾏
  2. apply_async:apply的异步版本
  3. terminate:⽴刻关闭进程池
  4. join:主进程等待所有⼦进程执⾏完毕,必须在close或terminate之后
  5. close:等待所有进程结束后,才关闭进程池
import os
import time
from multiprocessing import Pool


def f(i):
    time.sleep(1)
    # 打印进程号
    print('子进程:', os.getpid())
    return i + 100


def g(n):
    print(n)
    print('回调函数:', os.getpid())


def main():
    pool = Pool(processes=3)
    print('主进程:', os.getpid())
    # pool.apply() 同步执行
    # 异步执行
    # 回调函数是主进程在处理
    for i in range(5):
        pool.apply_async(func=f, args=(i,), callback=g)
    pool.close()
    pool.join()


if __name__ == '__main__':
    main()

协程greenlet

greenlet是⼀个⽤C实现的协程模块,相⽐与python⾃带的yield,它可以使你在任意函数之间随意切
换,⽽不需把这个函数先声明为generator

from greenlet import greenlet


def f1():
    print(11)
    g2.switch()
    print(12)
    g2.switch()


def f2():
    print(21)
    g1.switch()
    print(22)


g1 = greenlet(f1)
g2 = greenlet(f2)
g1.switch()

Gevent

Gevent 是⼀个第三⽅库,可以轻松通过gevent实现并发同步或异步编程,在gevent中⽤到的主要模式
是Greenlet, 它是以C扩展模块形式接⼊Python的轻量级协程。

import time

import gevent


def f1():
    print('start f1')
    gevent.sleep(10)
    print('end f1')


def f2():
    print('start f2')
    print('end f2')


def f3():
    print('start f3')
    gevent.sleep(20)
    print('end f3')


def main():
    gevent.joinall([gevent.spawn(f1), gevent.spawn(f2), gevent.spawn(f3)])


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

推荐阅读更多精彩内容

  • 线程 操作系统线程理论 线程概念的引入背景 进程 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有...
    go以恒阅读 1,641评论 0 6
  • 一文读懂Python多线程 1、线程和进程 计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运...
    星丶雲阅读 1,453评论 0 4
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,454评论 1 15
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,957评论 1 18
  • 【threading模块详解】 模块基本方法 该模块定了的方法如下:threading.active_count(...
    奕剑听雨阅读 1,043评论 0 0