并发编程

并发编程(concurrent programming)

  1. 提升执行性能 - 让程序中没有因果关系的部分可以并发的执行
  2. 改善用户体验 - 让耗时间的操作不会造成程序的假死

Python中实现并发编程的三种手段:

  1. 多线程 - 多个线程可以共享操作系统分配给进程的内存 - 线程通信相对简单
  2. 多进程 - 进程之间的内存是不共享的 - 管道 / 套接字 / 中间件
  3. 异步I/O - 非阻塞式的I/O操作,通过回调函数对操作结果进行处理
  • Redis / Node.js - 多路I/O复用(I/O操作是多路并发)

面试题:进程和线程的区别和联系?
进程 - 操作系统分配内存的基本单位 - 一个进程可以包含一个或多个线程
线程 - 操作系统分配CPU的基本单位

分别使用多线程和多进程生成图片缩率图

import threading
import multiprocessing

from PIL import Image
import glob, os

PREFIX = 'thumbnails'


def generate_thumbnail(infile, size, format='JPEG'):
    file, ext = os.path.splitext(infile)
    file = file[file.rfind('/') - 1:]
    outfile = f'./{PREFIX}/{file}_{size[0]}_{size[1]}{ext}'
    im = Image.open(infile)
    im.thumbnail(size, Image.ANTIALIAS)
    im.save(outfile, format)


def main():
    if not os.path.exists(PREFIX):
        os.mkdir(PREFIX)
    for infile in glob.glob('images/*.jpg'):
        for size in (32, 64, 128):
            # 多线程(IO密集型任务)
            # threading.Thread(
            #     target=generate_thumbnail,
            #     args=(infile, (size, size))
            # ).start()
            # 多进程(计算密集型任务)
            multiprocessing.Process(
                target=generate_thumbnail,
                args=(infile, (size, size))
            ).start()


if __name__ == '__main__':
    main()

解决多线程引起的资源竞争问题

多线程程序如果没有竞争资源的场景那么通常会比较简单
临界资源 - 被多个线程竞争的资源
当多个线程竞争临界资源的时候如果缺乏必要的保护措施就会导致数据错乱

import threading
import time
from concurrent.futures import ThreadPoolExecutor

class Account(object):

    def __init__(self):
        self.balance = 0.0
        self.lock = threading.Lock()

    def deposit(self, money):
        # 添加线程锁进行保护
        # self.lock.acquire()
        # try:
        #     new_balance = self.balance + money
        #     time.sleep(0.01)
        #     self.balance = new_balance
        # finally:
        #     self.lock.release()
        # 上下文语法优化
        with self.lock:
            new_balance = self.balance + money
            time.sleep(0.01)
            self.balance = new_balance


def add_money(account, money):
    account.deposit(money)


class AddMoneyThread(threading.Thread):

    def __init__(self, account, money):
        # 自定义线程的初始化方法中必须调用父类的初始化方法    
        super().__init__()
        self.account = account
        self.money = money

    # run方法是线程启动之后要执行的回调方法(钩子函数)
    # 所以启动线程不能够直接调用run方法而是通过start方法启动线程
    # 什么时候需要使用回调式编程?
    # 你知道要做什么但不知道什么时候会做这件事情
    def run(self):
        self.account.deposit(self.money)

def main():
    account = Account()
    # 创建线程池
    pool = ThreadPoolExecutor(max_workers=10)
    futures = []
    for _ in range(100):
        # 创建线程的第1种方式
        # threading.Thread(
        #     target=add_money, args=(account, 1)
        # ).start()
        # 创建线程的第2种方式
        # AddMoneyThread(account, 1).start()
        # 创建线程的第3种方式
        # 调用线程池中的线程来执行特定的任务
        future = pool.submit(add_money, account, 1)
        futures.append(future)
    # 关闭线程池
    pool.shutdown()
    for future in futures:
        future.result()
    print(account.balance)


if __name__ == '__main__':
    main()

守护线程/守护进程

  • daemon

mysqld / httpd / firewalld / systemd
如果主线程结束了守护线程也不再保留即使守护线程还在执行(没有结束)
Lock - 多个线程竞争临界资源(资源只有1个) - 获得锁才能操作资源
Condition - 基于Lock对象可以创建它 - wait() / notify_all() -
实现线程调度
Semaphore - 多个线程竞争资源(资源有多个,但线程数量大于资源数量)
多个线程通信比较简单因为可以共享内存
多个进程通信相对比较困难,可以使用multiprocessing.Queue
通过多个进程共享一个队列来实现进程间的通信
生产者消费者模型 / 哲学家进餐模型 - 多线程编程模型

import threading


def show_message(content):
    while True:
        print(content, end=' ')


def main():
    threading.Thread(
        target=show_message,
        args=('Ping',),
        daemon=True
    ).start()
    threading.Thread(
        target=show_message,
        args=('Pong',),
        daemon=True
    ).start()


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

推荐阅读更多精彩内容