zoukankan      html  css  js  c++  java
  • 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 模块提供异步执行回调高层接口。
    Nobody knows it better than me.
  • 相关阅读:
    BZOJ3048: [Usaco2013 Jan]Cow Lineup
    BZOJ1605: [Usaco2008 Open]Crisis on the Farm 牧场危机
    BZOJ3887: [Usaco2015 Jan]Grass Cownoisseur
    BZOJ5055: 膜法师
    BZOJ2037: [Sdoi2008]Sue的小球
    BZOJ1722: [Usaco2006 Mar] Milk Team Select 产奶比赛
    Uva 11054 Wine trading in Gergovia
    poj 2549 Sumsets
    Uva 1326 Jurassic Remains
    Uva 10755 Garbage Heap
  • 原文地址:https://www.cnblogs.com/dadaizi/p/13060427.html
Copyright © 2011-2022 走看看