python进阶-多线程和多进程巩固

进程

  • 进程的概念

进程就是操作系统中执行的一个程序,操作系统以进程为单位分配存储空间,每个进程都有自己的地址空间、数据栈以及其他用于跟踪进程执行的辅助数据,操作系统管理所有进程的执行,为它们合理的分配资源。进程可以通过fork或spawn的方式来创建新的进程来执行其他的任务,不过新的进程也有自己独立的内存空间,因此必须通过进程间通信机制(IPC,Inter-Process Communication)来实现数据共享,具体的方式包括管道、信号、套接字、共享内存区等。

python中的多进程

Unix和Linux操作系统上提供了fork()系统调用来创建进程,调用fork()函数的是父进程,创建出的是子进程,子进程是父进程的一个拷贝,但是子进程拥有自己的PID。fork()函数非常特殊它会返回两次,父进程中可以通过fork()函数的返回值得到子进程的PID,而子进程中的返回值永远都是0。Python的os模块提供了fork()函数。由于Windows系统没有fork()调用,因此要实现跨平台的多进程编程,可以使用multiprocessing模块的Process类来创建子进程,而且该模块还提供了更高级的封装,例如批量启动进程的进程池(Pool)、用于进程间通信的队列(Queue)和管道(Pipe)等

  • 不使用多进程下载文件
from random import randint
from time import time, sleep


def download_task(filename):
    print("开始下载%s..." % filename)
    time_to_download = randint(5,10)
    sleep(time_to_download)
    print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))


def main():
    # 开始时间
    start = time()
    # 下载文件
    download_task('python从入门到放弃.pdf')
    download_task('Peking Hot.avi')
    # 结束时间
    end = time()
    print("总共耗费了%.2f秒." % (end - start))


if __name__ == '__main__':
    main()

运行结果:

开始下载python从入门到放弃.pdf...
python从入门到放弃.pdf下载完成! 耗费了10秒
开始下载Peking Hot.avi...
Peking Hot.avi下载完成! 耗费了5秒
总共耗费了15.00.

从上面的例子可以看出, 程序中的代码只能按顺序一点点的往下执行, 一个文件下载完成后才能开始下一个任务.

  • 使用多进程
from random import randint
from time import time, sleep
from multiprocessing import Process
from os import getpid


def download_task(filename):
    print('启动下载进程, 进程号[%d]' % getpid())
    print("开始下载%s..." % filename)
    time_to_download = randint(5,10)
    sleep(time_to_download)
    print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))


def main():
    # 开始时间
    start = time()
    # 创建多进程
    p1 = Process(target=download_task, args=('python从入门到放弃.pdf',))
    p1.start()
    p2 = Process(target=download_task, args=('Peking Hot.avi',))
    p2.start()
    p1.join()
    p2.join()
    # 结束时间
    end = time()
    print("总共耗费了%.2f秒." % (end - start))


if __name__ == '__main__':
    main()

在上面的代码中, 我们通过Process类创建了进程对象, 通过target参数我们传入一个函数来表示进程启动后要执行的代码,
后面的args是一个元组, 它代表了传递给函数的参数, Process对象的start方法用来启动进程,
而join方法表示等待进程执行结束.

python 中的多线程

目前的多线程开发推荐使用threading模块,该模块对多线程编程提供了更好的面向对象的封装

  • 使用多线程下载文件
from time import time,sleep
from threading import Thread
from random import randint


def download(filename):
    print('开始下载%s...' % filename)
    time_to_download = randint(5, 10)
    sleep(time_to_download)
    print('%s下载完成, 耗费了%d秒' % (filename, time_to_download))


def main():
    start = time()
    t1 = Thread(target=download, args=('Python从入门到放弃',))
    t1.start()
    t2 = Thread(target=download, args=('Peking hot.avi',))
    t2.start()
    t1.join()
    t2.join()
    end = time()
    print('总共耗费了%.3f秒' % (end - start))


if __name__ == '__main__':
    main()

多个线程共享同一个变量的时候, 很有可能产生不可控的结果从而导致程序失效甚至崩溃.如果一个资源被多个线程竞争使用, 我们需要对 临界资源 的访问加上保护.

  • 100个线程向同一个银行账户转账的场景
from threading import Thread, Lock
from time import sleep


class Account(object):
    def __init__(self):
        self._balance = 0
        self._lock = Lock()

    def deposit(self, money):
        # 先获取锁才能执行后续的代码
        self._lock.acquire()
        try:
            # 计算存款后的余额
            new_balance = self._balance + money
            # 模拟受理存款业务需要0.01秒时间
            sleep(0.01)
            # 修改账户余额
            self._balance = new_balance
        finally:
            self._lock.release()

    @property
    def balance(self):
        return self._balance


class AddMoneyThread(Thread):
    def __init__(self, account, money):
        super().__init__()
        self._account = account
        self._money = money

    def run(self):
        self._account.deposit(self._money)


def main():
    # 计算余额
    account = Account()
    threads = []
    # 创建100个存款的线程向同一个账户中存钱
    for _ in range(100):
        # 添加金额
        t = AddMoneyThread(account, 1)
        threads.append(t)
        t.start()
    # 等所有存款的线程都执行完毕
    for t in threads:
        t.join()
    print('账户余额为: %d元' % account.balance)


if __name__ == '__main__':
    main()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值