python的多线程和多进程

Posted dai_213

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python的多线程和多进程相关的知识,希望对你有一定的参考价值。

Python多线程和多进程

1、简介线程和进程

1)线程
    线程(有时候成为轻量级进程)与进程类似,不过它们是在同一个进程下执行的,并且共享相同的上下文,可以将他们认为是在一个主进程或者“主线程”中并行运行的一些“迷你进程”。
    线程包括,开始、执行顺序和结束三个部分。它有一个指令指针,用于记录当前运行的上下文。当其他线程运行时,它可以被抢占(中断)和临时挂起(也称为睡眠)--这种做法叫做让步(yielding)
    线程无法给予公平的执行时间。这是因为一些函数会在完成前保持阻塞状态,如果没有专门为多线程情况进行修改,会导致 CPU 的时间分配向这些贪婪的函数倾斜。
2)进程
    计算机程序只是存储在磁盘上的可执行二进制(或其他类型)文件。只有把他们加载到内存并被操作系统调用,才拥有生命周期。进程(有时称为重量级进程)则是一个执行中的程序。每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据。
操作系统管理其上所有进程的执行,并为这些进程合理地分配时间。进程也可以通过派生
(fork 或 spawn)新的进程来执行其他任务,不过因为每个新进程也都拥有自己的内存和数据栈等,所以只能采用进程间通信(IPC)的方式共享信息。

2、python和线程

1)全局解释器锁
    Python 代码的执行是由 Python 虚拟机(又名解释器主循环)进行控制的。Python 在
设计时是这样考虑的,在主循环中同时只能有一个控制线程在执行,就像单核 CPU 系统
中的多进程一样。内存中可以有许多程序,但是在任意给定时刻只能有一个程序在运行。
同理,尽管 Python 解释器中可以运行多个线程,但是在任意给定时刻只有一个线程会被
解释器执行。
    对 Python 虚拟机的访问是由全局解释器锁(GIL)控制的。这个锁就是用来保证同时只
能有一个线程运行的。在多线程环境中,Python 虚拟机将按照下面所述的方式执行。
1.设置GIL
2.切换进一个线程去运行。
3.执行下面操作之一
    a)指定数量的字节码指令
    b)线程主动让出控制权(可以调用time.sleep(0)来完成)
4.把线程设置会睡眠状态(切换出线程)
5.解锁GIL
6.重复上述步骤
由于以上特性,我们可以看出,例如,对于任意面向 I/O 的 Python 例程(调用了内置的操作系统 C 代码的那种),GIL 会在 I/O 调用前被释放,以允许其他线程在 I/O 执行的时候运行。而对于那些没有太多 I/O 操作的代码而言,更倾向于在该线程整个时间片内始终占有处理器(和 GIL)。换句话说就是,I/O 密集型的 Python 程序要比计算密集型的代码能够更好地利用多线程环境。
2)python的threading模块
threading模块中有很多可用的对象, Thread,Lock等等
(a) Thread类,threaddng模块中的Thread类,是主要的执行对象。它有很多函数属性技术图片
 
使用 Thread 类,可以有很多方法来创建线程。我们将介绍其中比较相似的三种方法。选
择你觉得最舒服的,或者是最适合你的应用和未来扩展的方法(我们更倾向于最后一种方案)。
• 创建 Thread 的实例,传给它一个函数。

 
 
 
 
 
 
 
 
 
 
 
1
"""
2
多线程实现方式一:
3
创建 Thread 的实例,传给它一个函数。
4
时间:2019年11月24日
5
author:戴昊龙
6
"""
7
from threading import Thread
8
from time import sleep, ctime
9

10
loops = [4, 2]
11

12

13
def loop(nloop, nsec):
14
    print(‘start loop‘, nloop, ‘at:‘, ctime())
15
    sleep(nsec)
16
    print(‘loop‘, nloop, ‘done at‘, ctime())
17

18

19
def main():
20
    print("starting at:", ctime())
21
    threads = []  # 设置线程数
22
    nloops = range(len(loops))
23
    for i in nloops:
24
        t = Thread(target=loop, args=(i, loops[i])) # target传入的是待执行多线程的函数名,args传入的是这个函数需要的参数
25
        threads.append(t)
26
    for i in nloops:
27
        threads[i].start() # 开始执行这个线程
28
    for i in nloops:
29
        threads[i].join() # join() 方法,可以让主线程等待所有线程执行完毕。
30
    print("all done at", ctime())
31

32

33
if __name__ == ‘__main__‘:
34
    main()
 
 
• 派生 Thread 的子类,并创建子类的实例。

 
 
 
 
 
 
 
 
 
 
 
1
"""
2
子类化的Thread,来实现多线程
3
时间:2019年11月24日
4
author:戴昊龙
5
"""
6
from threading import Thread
7
from time import ctime, sleep
8
loops = [4, 2]
9

10

11
class MyThread(Thread):
12
    def __init__(self, func, agrs, name=‘‘):
13
        super().__init__()  # 等价于super(MyThread, self).__init__()
14

15
        # 上面这个super().__init__() 为什么没有填父类的参数? 不是说要完全继承父类的属性吗?怎么子类非但没有继承,还自己加了三个属性?
16
        # 这个是因为Thread类,中init方法,含有默认参数,父类的属性均已传默认参数
17
        """
18
        阅读源码可知def __init__(self, group=None, target=None, name=None,
19
                 args=(), kwargs=None, *, daemon=None):
20
        因此子类继承的时候,只需要简写就可以了,会默认全部继承,子类需要新加属性,也只需要自己在init方法中新增就可以
21
        但是还是得调用父类的init方法,只是可以简单的省略其中的参数
22
        """
23
        self.func = func
24
        self.args = agrs
25
        self.name = name
26

27

28
    def run(self):
29
        self.func(*self.args)
30

31

32
def loop(nloop, nsec):
33
    print("start loop", nloop, ‘at:‘, ctime())
34
    sleep(nsec)
35
    print(‘loop‘, nloop, "done at", ctime())
36

37

38
def main():
39
    print(‘starting at:‘, ctime())
40
    threads = []
41
    nloops = range(len(loops))
42
    for i in nloops:
43
        t = MyThread(loop, (i, loops[i]), loop.__name__)
44
        threads.append(t)
45
    for i in nloops:
46
        threads[i].start()
47
    for i in nloops:
48
        threads[i].join()
49
    print("all done at:", ctime())
50

51

52
if __name__ == ‘__main__‘:
53
    main()
 
 

3、多线程实践

1)同步原语
一般在多线程代码中,总会有一些特定的函数或代码块不希望(或不应该)被多个线程同时执行,通常包括修改数据库、更新文件或其他会产生竞态条件的类似情况。
当 任 意 数 量 的 线 程 可 以 访 问 临 界 区 的 代 码但在给定的时刻只有一个线程可以通过时,就是使用同步的时候了。程序员选择适合的同步原语,或者线程控制机制来执行同步。
使用其中两种类型的同步原语演示几个示例程序:锁/互斥,以及信号量。锁是所有机制中最简单、最低级的机制,而信号量用于多线程竞争
有限资源的情况。锁比较容易理解,因此先从锁开始,然后再讨论信号量。

 
 
 
 
 
 
 
 
 
 
 
1
# 不加锁的情况下
2
from threading import Thread
3
from random import randint
4
from time import ctime, sleep
5
list1 = [‘dai‘, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘shuai‘, ‘bi‘]
6

7

8
def modify_list():
9
    print("现在时间是%s, 当前的list是:" % ctime(), list1 )
10
    sleep(1)
11
    list1.insert(0, randint(0,10))
12
    sleep(1)
13
    print("执行insert之后现在时间是%s, 当前的list是:" % ctime(), list1)
14
    sleep(1)
15
    list1.pop(randint(0, len(list1)))
16
    sleep(1)
17
    print("执行pop之后现在时间是%s, 当前的list是:" % ctime(), list1)
18

19
    return list1
20

21

22
for x in range(5):
23
    Thread(target=modify_list, args=()).start()
24
    # modify_list()
25

26
"""
27
I:python_auto_testpytest_taskvenvScriptspython.exe I:/python_auto_test/pytest_task/thread_demo.py
28
现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [‘dai‘, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘shuai‘, ‘bi‘]
29
Thread-1
30
执行insert之后现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [5, ‘dai‘, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘shuai‘, ‘bi‘]
31
现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [5, ‘dai‘, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘shuai‘, ‘bi‘]
32
执行pop之后现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [5, ‘dai‘, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
33
Thread-2
34
执行insert之后现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [4, 5, ‘dai‘, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
35
执行pop之后现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [4, 5, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
36
现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [4, 5, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
37
Thread-3
38
现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [4, 5, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
39
执行insert之后现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [1, 4, 5, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
40
执行pop之后现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [1, 4, 5, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘]
41
Thread-4
42
执行insert之后现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [2, 1, 4, 5, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘]
43
现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [2, 1, 4, 5, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘]
44
执行pop之后现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [2, 1, 4, 5, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘]
45
Thread-5
46
执行insert之后现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [0, 2, 1, 4, 5, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘]
47
执行pop之后现在时间是Sun Dec  1 16:52:42 2019, 当前的list是: [0, 2, 1, 4, 5, ‘long‘, ‘shi‘, ‘ge‘]
48

49
Process finished with exit code 0
50

51
"""
 
 
 

 
 
 
 
 
 
 
 
 
 
 
1
# 加锁的情况下
2
from threading import Thread, Lock
3
from random import randint
4
from time import ctime, sleep
5
list1 = [‘dai‘, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘shuai‘, ‘bi‘]
6
lock = Lock()
7

8

9
def modify_list():
10
    print("现在时间是%s, 当前的list是:" % ctime(), list1 )
11
    lock.acquire()
12
    list1.insert(0, randint(0,10))
13
    print("执行insert之后现在时间是%s, 当前的list是:" % ctime(), list1)
14
    list1.pop(randint(0, len(list1)))
15
    lock.release()
16
    print("执行pop之后现在时间是%s, 当前的list是:" % ctime(), list1)
17

18
    return list1
19

20

21
for x in range(5):
22
    Thread(target=modify_list, args=()).start()
23
    # modify_list()
24

25

26
"""
27
现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [‘dai‘, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘shuai‘, ‘bi‘]
28
Thread-1
29
执行insert之后现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [4, ‘dai‘, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘shuai‘, ‘bi‘]
30
现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [4, ‘dai‘, ‘hao‘, ‘long‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘shuai‘, ‘bi‘]
31
执行pop之后现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [4, ‘dai‘, ‘hao‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘shuai‘, ‘bi‘]
32
Thread-2
33
执行insert之后现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [7, 4, ‘dai‘, ‘hao‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘shuai‘, ‘bi‘]
34
执行pop之后现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [7, 4, ‘dai‘, ‘hao‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
35
现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [7, 4, ‘dai‘, ‘hao‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
36
Thread-3
37
执行insert之后现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [2, 7, 4, ‘dai‘, ‘hao‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
38
执行pop之后现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [2, 7, 4, ‘dai‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
39
现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [2, 7, 4, ‘dai‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
40
Thread-4
41
现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [2, 7, 4, ‘dai‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
42
执行insert之后现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [10, 2, 7, 4, ‘dai‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
43
执行pop之后现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [2, 7, 4, ‘dai‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
44
Thread-5
45
执行insert之后现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [8, 2, 7, 4, ‘dai‘, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
46
执行pop之后现在时间是Sun Dec  1 16:52:04 2019, 当前的list是: [8, 2, 7, 4, ‘wo‘, ‘shi‘, ‘ge‘, ‘bi‘]
47
"""
 
 
信号量
 

4、多进程实践

concurrent.futures 模块提供异步执行回调高层接口。

以上是关于python的多线程和多进程的主要内容,如果未能解决你的问题,请参考以下文章

Python的多线程和多进程模块对比测试

python的多线程和多进程

python的多线程和多进程

python的多线程和多进程

什么是多线程,多进程?

Python的多线程threading和多进程multiprocessing