美文网首页算法实习生
python中的进程与线程

python中的进程与线程

作者: STACK_ZHAO | 来源:发表于2019-07-12 22:33 被阅读0次

进程与线程的定义

一句话来叙述

简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

对于进行与线程的区别,之前在整理并行的时候整理过一次,这里再稍微解释下,

线程是CPU调度的基本单位,进程是操作系统分配资源的单位

这句话可以了解到一个比较简单的区分,

  1. 进程的行使资源的范围是大于线程的,一个进程里面可以有多个线程,每个进程如独特的地址与内存资源,而线程一般都是局部变量,实现变量的临时创建于共享,
  2. 线程也是有优点的,线程的并存性能,决定了一个进程里面的多个线程可以实现并行的计算,进程在python中可以中fork来进行创建,一般而言一个进程里面的多个线程都是处理属于这一个进程的任务,而多个进程一般都是处理同一个应用的不同任务,比如如下图
一个谷歌应用有多个进程再运行

最后可以举一个火车的例子,一辆火车有多个车厢,每个车厢里面有很多人,火车就是一个电脑,而车厢就是多个进程,每个车厢里面的人就是多个线程。

python 中的多进程与多线程

python中的多进程可以通过两个方法来实现,首先是python的OS原生的方法,fork()一个新的进程,但是这个在跨平台的时候可能会出现一定的问题,在这里,我推荐用一个多进程的包,那就是multiprocessing模块的Process类来创建子进程,例子如下所示

from multiprocessing import Process
from os import getpid
from random import randint
from time import time, sleep


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()
#join([timeout])可以参考Python文档说明。
   #大概意思就是调用join函数会使得主调线程阻塞,直到被调用线程运行结束或超时。
   #参数timeout是一个数值类型,用来表示超时时间,如果未提供该参数,
   #那么主调线程将一直阻塞直到被调用线程结束。
    p2.join()
    end = time()
    print('总共耗费了%.2f秒.' % (end - start))


if __name__ == '__main__':
    main()

其中,通过process函数创建了两个不同的进程,两个进程同时运行,从而节省了时间

多线程

python中的多线程,是很好实现的,主要是通过进程threading包中的Tread类来实现对多个线程的创建,例子如下所示,其实跟多进程很相似,都可以创建一个继承于Thread的线程类然后运行,或者是直接调用Thread函数,跟Process的用法是一样的:

from random import randint
from threading import Thread
from time import time,sleep
class DownloadTask(Thread):
    def __init__(self,filename):
        super().__init__()
        self._filename=filename
    def run(self):
        print('开始下载%s...'%self._filename)
        time_to_download=randint(5,10)
        sleep(time_to_download)
        print('%s下载完成!消耗%d秒'%(self._filename,time_to_download))
def main():
    start=time()
    t1=DownloadTask('python 是智障')
    t1.start()#start() 方法是启动一个子线程,这里做个小提示,注意区分start()跟run()的区别,run不会创建一个新的子进程,如果你直接run()的话,一般是直接运行main thread
    t2=DownloadTask('gaygay 成是gay')
    t2.start()
    t1.join()
    t2.join()
    end=time()
    print('总共消耗了%.2f秒.'%(end-start))

  #  print('总共耗费了%.2f秒.' % (end - start))
if __name__=='__main__':
    main()

上面就是多个线程的实现。

临界资源加锁的实现

在多个进程同时运行的时候,变量的共享是一个问题,有的变量是局部变量,有的是全局变量,但是全局变量如果多个线程多是占用的时候,很容易发生变量错误的结果,这种的全局变量在一定程度上也是临界资源,比如多个人同时向一个用户的账号打钱,如果同时调用,那么最后的账户可能是远远小于预想的结果的。这里补充下临界资源的定义

多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次只能供一个进程使用
这种资源就是临界资源

那么怎么处理临界资源呢,其实学过操作系统的同学可能知道锁的概念,以及如何加锁,以及死锁的情况和解决方法,在这里,python中是一样的道理,这里用多个用户向一个账户里打钱的例子来解释,主要是用到i了thread种的lack函数:

from time import sleep
from threading import Thread, Lock


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
            sleep(0.01)
            self._balance = new_balance
        finally:
            # 在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 = []
    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()

但是Python的多线程并不能发挥CPU的多核特性,之所以如此,是因为Python的解释器有一个“全局解释器锁”(GIL)的东西,任何线程执行前必须先获得GIL锁,然后每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行,这个就不如c语言那样灵活了。所以在这个地方,友好的是可以用python调c中的并行计算MPI包来实现,个人觉得还是很好用的,具体如何调用,下次更新的时候进行详细的介绍。
但是python里面的多进程与多线程并不是没用用的,其实他在异步I/O的时候可以使用多个进程进程处理,而在实现分治法处理大容量数据的时候,还是可以用的。
本文中的例子来自于
Python - 100天从新手到大师

相关文章

网友评论

    本文标题:python中的进程与线程

    本文链接:https://www.haomeiwen.com/subject/hkgxkctx.html