Day08 : python线程与进程

在这里插入图片描述

一、序

  在之前编写的程序中,我们写过的大多数程序都是在一个地方(一台机器)一次运行一行(解释型顺序执行)。但是,我们可以在多个地方(分布式计算或者网络)同时做很多事(并发)。这样做有很多好处。下面记录几个常见词汇的含义:

  • 鲁棒性
    数量可以提高安全性,可以同时执行多个相同任务,这样就不用担心出现硬件和软件
    错误。
  • I/O 限制
    这个限制很常见。计算机的 CPU 速度非常快——比计算机内存快几百倍,比硬盘或者网络快几千倍。
  • 同步
    一件事接着一件事发生,就像送葬队伍一样。
  • 异步
    任务是互相独立的,就像派对参与者从不同的车上下来一样。

二、进程与线程

  进程我们可以理解为是一个可以独立运行的程序单位,比如打开一个浏览器,这就开启了一个浏览器进程;打开一个文本编辑器,这就开启了一个文本编辑器进程。但一个进程中是可以同时处理很多事情的,比如在浏览器中,我们可以在多个选项卡中打开多个页面,有的页面在播放音乐,有的页面在播放视频,有的网页在播放动画,它们可以同时运行,互不干扰。为什么能同时做到同时运行这么多的任务呢?这里就需要引出线程的概念了,其实这一个个任务,实际上就对应着一个个线程的执行。
  而进程呢?它就是线程的集合,进程就是由一个或多个线程构成的,线程是操作系统进行运算调度的最小单位,是进程中的一个最小运行单元。比如上面所说的浏览器进程,其中的播放音乐就是一个线程,播放视频也是一个线程,当然其中还有很多其他的线程在同时运行,这些线程的并发或并行执行最后使得整个浏览器可以同时运行这么多的任务。
在这里插入图片描述

三、并发和并行

1. 并发( concurrency)

  它是指同一时刻只能有一条指令执行,但是多个线程的对应的指令被快速轮换地执行。比如一个处理器,它先执行线程 A 的指令一段时间,再执行线程 B 的指令一段时间,再切回到线程 A 执行一段时间。
  由于处理器执行指令的速度和切换的速度非常非常快,人完全感知不到计算机在这个过程中有多个线程切换上下文执行的操作,这就使得宏观上看起来多个线程在同时运行。但微观上只是这个处理器在连续不断地在多个线程之间切换和执行,每个线程的执行一定会占用这个处理器一个时间片段,同一时刻,其实只有一个线程在执行。

2. 并行(parallel)

  它是指同一时刻,有多条指令在多个处理器上同时执行,并行必须要依赖于多个处理器。不论是从宏观上还是微观上,多个线程都是在同一时刻一起执行的。
  并行只能在多处理器系统中存在,如果我们的计算机处理器只有一个核,那就不可能实现并行。而并发在单处理器和多处理器系统中都是可以存在的,因为仅靠一个核,就可以实现并发。
  举个例子,比如系统处理器需要同时运行多个线程。如果系统处理器只有一个核,那它只能通过并发的方式来运行这些线程。如果系统处理器有多个核,当一个核在执行一个线程时,另一个核可以执行另一个线程,这样这两个线程就实现了并行执行,当然其他的线程也可能和另外的线程处在同一个核上执行,它们之间就是并发执行。具体的执行方式,就取决于操作系统的调度了。

四、多线程

1. 使用threading模块

写个demo测试一下:

单线程执行:

#coding=utf-8
import time

def say_sorry():
    print("亲爱的,我错了,我能吃饭了吗?")
    time.sleep(1)

if __name__ == "__main__":
    for i in range(5):
        say_sorry()

然后我们来看一下效果
在这里插入图片描述

多线程执行:

#coding=utf-8
import threading
import time

def say_sorry():
    print("亲爱的,我错了,我能吃饭了吗?")
    time.sleep(1)

if __name__ == "__main__":
    for i in range(5):
        t = threading.Thread(target=say_sorry)
        t.start() #启动线程,即让线程开始执行

再来看一下效果
在这里插入图片描述

2. 查看线程数量

调用threading.enumerate()方法,可以查看当前正在运行的线程,该方法返回一个列表
这里我们再来写一个唱、跳rap的代码:

import threading
from time import sleep, ctime


def sing():
    for i in range(3):
        print("正在唱歌...%d" % i)
        sleep(1)


def dance():
    for i in range(3):
        print("正在跳舞...%d" % i)
        sleep(1)


if __name__ == '__main__':
    print('---开始---:\n%s' % ctime())

    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=dance)

    t1.start()
    t2.start()

    while True:
        length = len(threading.enumerate())
        print(threading.enumerate())
        print('当前运行的线程数为:%d' % length)
        if length <= 1:
            break

        sleep(0.5)
        
结果:
---开始---:
Fri May 29 08:37:59 2020
正在唱歌...0
正在跳舞...0
[<_MainThread(MainThread, started 11744)>, <Thread(Thread-1, started 3992)>, <Thread(Thread-2, started 7696)>]
当前运行的线程数为:3
[<_MainThread(MainThread, started 11744)>, <Thread(Thread-1, started 3992)>, <Thread(Thread-2, started 7696)>]
当前运行的线程数为:3
正在唱歌...1
正在跳舞...1
[<_MainThread(MainThread, started 11744)>, <Thread(Thread-1, started 3992)>, <Thread(Thread-2, started 7696)>]
当前运行的线程数为:3
[<_MainThread(MainThread, started 11744)>, <Thread(Thread-1, started 3992)>, <Thread(Thread-2, started 7696)>]
当前运行的线程数为:3
正在唱歌...2
[<_MainThread(MainThread, started 11744)>, <Thread(Thread-1, started 3992)>, <Thread(Thread-2, started 7696)>]
当前运行的线程数为:3
正在跳舞...2
[<_MainThread(MainThread, started 11744)>, <Thread(Thread-1, started 3992)>, <Thread(Thread-2, started 7696)>]
当前运行的线程数为:3
[<_MainThread(MainThread, started 11744)>, <Thread(Thread-2, started 7696)>]
当前运行的线程数为:2
[<_MainThread(MainThread, started 11744)>]
当前运行的线程数为:1

Process finished with exit code 0

这个地方看起来还是比较清晰的。

3. 一个不太重要的结论

  在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据。缺点就是,线程是对全局变量随意篡改可能造成多线程之间对全局变量的混乱(即线程非安全)

五、多进程

1. 进程

一个程序运行起来后,代码+用到的资源 称之为进程,它是操作系统分配资源的基本单元。不仅可以通过线程完成多任务,进程也是可以的

2. 进程的状态

在这里插入图片描述

  • 就绪态:运行的条件都已经慢去,正在等在cpu执行
  • 执行态:cpu正在执行其功能
  • 等待态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态

3. 使用multiprocessing模块

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time


def run_proc():
    """子进程要执行的代码"""
    while True:
        print("----2----")
        time.sleep(1)


if __name__=='__main__':
    p = Process(target=run_proc)
    p.start()
    while True:
        print("----1----")
        time.sleep(1)

4. 进程池

  当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。

  初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务。

from multiprocessing import Pool
def show(num):
    print('num : ' + str(num))

if __name__=="__main__":
    pool = Pool(processes = 3)
    for i in xrange(6):
        # 维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
        pool.apply_async(show, args=(i, ))       
    print('======  apply_async  ======')
    pool.close()
    #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
    pool.join()

六、学习体会

  多任务在什么语言中都是比较重要的概念,同时也都有很丰富的应用场景。不过刚开始接触python的时候,不是特别容易理解。做个简单的了解就行,用到的时候再去具体学习,千万不要被搞没积极性了!!!!!!

加油!!!

你可以的!你总是这样相信着自己!
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值