python并发编程(一):多线程,多进程

‘‘‘多进程(线程)编程:         1. 进程和线程         2. 并发与并行         3. 同步和异步         4. 阻塞和非阻塞         5. 进程/线程的调度模型‘‘‘ # 综述

‘‘‘进程和线程:    1. 进程:          1) 是一个程序在数据集上的一次动态执行过程         2) 由程序, 数据集, 进程控制模块组成

    2. 线程:          1) 是CPU的一个最小执行单元,          2) 线程的出现是为了降低进程间切换的消耗         3) 实现在一个进程内的并发         4) 由线程ID, 程序计数器, 寄存器集合, 堆栈组成

    3. 进程和线程的关系:         1) 进程是线程的容器, 程序至少有一个进程, 一个进程至少有一个线程         2) 一个进程内的线程们共享进程资源, 线程几乎不拥有资源         3) 进程之间的资源不共享

    4. 多进程/多线程的切换方式:         1) IO阻塞         2) 时间轮询               ‘‘‘ # 进程和线程

‘‘‘并发和并行:    1. 并发:  系统具有处理多个任务的能力,(可能不是并行, 如快速切换)

    2. 并行:  系统具有‘同时‘处理多个任务的能力

    3. 并发和并行的关系:  并行是并发的子集‘‘‘ # 并发和并行

‘‘‘同步和异步:    1. 同步: 当进程(线程)执行IO操作时, 没得到结果就等待

    2. 异步: 当进程(线程)执行IO操作时, 不等待, 数据接收成功后, 再回来处理

    3. 同步和异步时针对任务的调度方式而言(等待/不等待)‘‘‘ # 同步和异步

‘‘‘阻塞和非阻塞:    1. 阻塞: 当进程(线程)执行IO操作时, 该进程(线程)被挂起, 直到有结果时才被激活

    2. 非阻塞: 当进程(线程)执行IO操作时, 该进程(线程)不会被挂起, 没有结果也返回

    3. 阻塞和非阻塞时针对进程(线程)的调度(挂起/不挂起)‘‘‘ # 阻塞和非阻塞

‘‘‘进程(线程)的调度模型:      1. 进程(线程)的三种状态: 就绪, 执行, 阻塞     2. 调度模型:

                ------执 行--------------------                |        ^_______            |               等待IO             |       执行其他线程                |             执行该线程       |                v                |            |               阻 塞 ---有效IO--> 就 绪 <-------‘‘‘ # 进程(线程)调度模型

‘‘‘Python的GIL全局解释器锁    1. Python没有多线程: 同一个时刻, 在一个进程内(无论有多少线程), Python解释器只允许执行一个线程

    2. 一般任务类型:                 1) IO密集型 --> IO切换(阻塞即切换)     无影响                 2) 计算密集型 --> 轮询切换             Python不适合用于计算密集型任务

    3. 解决GIL锁的方法:          多进程 + 协程 (但主要还是为IO密集型服务的)                     ‘‘‘ # GIL全局锁

‘‘‘多线程使用:     1. 导入模块 import threading     2. 开启线程的方法:        1) 直接开启:                    1> 创建Thread对象                     t = threading.Thread(target=func,args=(a,),kwargs={‘k1‘:v1})                  2> 开启线程                     t.start        2) 继承开启:                  1> 继承Thread类, 重写run()方法[即要执行的函数]                     class MyThread(threading.Thread):                          def __init__(self):                             super(MyThread,self).__init__()                             # 其他初始化代码,如要传给函数值                          def run(self): pass                  2. 创建实例,开启显线程                     t = MyThread()                     t.start()           # 会调用其run()方法

     3. 子线程和主线程的关系:        1> 主线程和子线程分别执行, 主线程会等待最后一个子线程执行完毕后, 关闭

        2> 子线程t.join()到主线程, 会阻塞主线程, 执行完该子线程后主线程继续执行, 最后主线程等待最后一个子线程执行完毕后, 关闭

        3> 在子线程开启之前, 申明该子线程为t.setDaemon(True)守护线程, 最后主线程不会等待该子线程, 其他线程执行完就关闭主线程

     4. 线程的属性和方法:        1) 对象属性和方法: t.attr t.func()                 t.func():                      t.start()          # 启动线程                     t.join()           # 阻塞当前线程                     t.setDaemon(True)  # 设置为守护线程                     t.run()            # 在当前线程下调用该线程的run方法

                     t.getName()        # 获取线程名                     t.setName()        # 设置线程名

                     t.isAlive()        # 判断线程是否被激活                     t.isDaemon()       # 判断线程是否为守护线程                    t.attr:                     t.name                # 线程名                     t.daemon = True/False # 是否为守护线程

        2) threading方法:             threading.currentThread()     # 当前线程对象                threading.enumerate()         # 返回一个List包含当前所有活跃的线程对象             thread.activeCount()          # 相等于len(threading.enumerate())                                                     ‘‘‘ # 多线程: 使用, 见Mulit_Threading.py

‘‘‘线程之间通信:一、同步锁(Lock):       1. 多线程操作公共变量的问题:一个线程还未来得及对公共变量进行操作, 就又有其他线程来访问该公共变量并修改了值

       2. 同步锁的作用:  部分串行               将一部分代码打包成一个整体(加锁), 同一个时刻只有获得锁的线程才能运行锁内的代码          Lock = threading.Lock()          Lock.acquire()          # code          Lock.release()

       3. GIL下的Lock:  线程A,线程B,线程C,...                       #1. 多线程争夺GIL, 若A抢到GIL的获得运行权限                       #2. 若A有Lock则执行锁内代码                       ...                       #3. 多线程继续争夺GIL, 若B抢到GIL                       #4. 此时A尚未执行完锁内代码, 锁未释放; B没有Lock, 阻塞                       ...                       #5. 直到A再次抢到GIL,执行完锁内代码,释放Lock       4. join和Lock:           t.join()是将该线程整体阻塞到当前线程, 即整体串行           Lock是部分串行

       5. 应用: 账户模型    

二、死锁:  同步锁不能解决锁中锁的问题      一把锁不能重用导致的, 后面的代码需要前面的锁, 但前面的锁已经被其他线程抢到了

      考虑: 线程A, 线程B, Lock_A(Lock_B), Lock_B(Lock_A)          1. 线程A先竞争到了Lock_A, 又竞争到了Lock_B, 执行锁中代码          2. 线程A执行完了, 顺序释放Lock_B, Lock_A          3. 线程A竞争到了Lock_B, 线程B竞争到了Lock_A          4. 死锁, 线程A在等Lock_A, 线程B在等Lock_B

三、递归锁(RLock): 解决死锁问题               可重入锁, 可递归使用, 没用完就不会释放      1. 原理:         1) RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。         2) 直到一个线程所有的acquire都被release,其他的线程才能获得资源      2.使用:          RLock = threading.RLock()          with RLock:              #code 1              with RLock:                  #code 2‘‘‘ # 多线程通信: 同步锁(threading.Lock), 死锁, 递归锁(threading.RLock)

‘‘‘同步对象(Event):        1.作用: 多线程运行中, 每个线程都是独立运行, Event保证其他线程需要等待某个线程的指令才能执行

       2. 使用: 通过event.wait()来阻塞线程,event.wait()后的代码必须等待某一个线程event.set()执行了才能执行              #1. Event = threading.Event()              #2. 指定某个线程为大哥, Event.set()发出指令              #3. 将其他线程设为小弟, Event.wait(), 要等待大哥发出指令后,Event.wait()之后代码才会执行                  执行后将Event.clear() 等待下一个指令       3. 方法:          Event.isSet()          Event.set()

          Event.wait()                      Event.clear()‘‘‘ # 多线程通信: 同步对象(threading.Event)

‘‘‘信号量(Semaphore):        1. 信号量与进(线)程池的区别:信号量是, 可以开启无限多的进程(线程), 但某一时刻只允许num个进程(线程)修改数据(涉及加锁)进程池是, 始终只开启num个进程(没有加锁)

       2. 使用:              #1. Semaphore = threading.Semaphore(num)    // 创建一个num线程的Lock              #2. Semaphore.acquire()                     // 从池子中取一个线程, 没有则阻塞                  # code                  Semaphore.release()                     // 释放一个空间‘‘‘ # 多线程通信: 信号量(threading.Semaphore)

‘‘‘线程队列(queue):        1. 线程安全:           1) list不是线程安全的数据类型          2) 保证线程安全的方法:                             1> 加锁                            2> 线程队列       2. 队列类型:          q = queue.Queue(num)              # 正常队列, FIFO          q = queue.LifoQueue(num)          # 栈, FILO          q = queue.PriorityQueue(num)      # 优先队列             : q.put([num,data])            # num从小到大, 优先级从高到低               q.get()                      # 返回一个list

       3. 线程队列保证线程安全

          q.put() # 向队列中添加数据, 队列为满则会阻塞       //           q.put_nowait() # 添加, 队列为满报异常          q.get() # 从队列中取数据, 队列为空则会阻塞         //           q.get_nowait() # 取值, 队列为空报异常

          另一种阻塞方式:          q.task_done() <--> q.join()

       4. 其他方法          q.qsize()   # 返回当前队列长度          q.empty()   # 队列是否为空          q.full()    # 队列是否为满‘‘‘ # 多线程通信: 线程队列(queue.Queue)

‘‘‘线程局部变量(ThreadLocal):    1. 作用: 每个线程只能访问公共变量, ThreadLocal为每个线程创建了自己的变量    2. 用法:           #1. thread_local = threading.local()    // 创建全局的thread_local对象           #2. thread_local.attr = value           // 在每个线程内, 线程可通过thread_local的属性设定变量                                                   // thread_local的相同属性在各个线程中不一样, 互不影响    3. 实质:       #1. thread_local对象内部维护一个dict, 其key为不同线程的ID, value为这些线程存储值       #2. thread_local.attr调用属性值时, 实际上是以当前线程的ID为key, 去找值‘‘‘ # 多线程通信: 线程局部变量(threading.local)

‘‘‘多进程使用:     1. 导入模块 import threading     2. 开启进程的方法:        1) 直接开启:                    1> 创建Process对象                     p = mulitprocessing.Process(target=func,args=(a,),kwargs={‘k1‘:v1})                  2> 开启线程                     p.start        2) 继承开启:                  1> 继承Process类, 重写run()方法[即要执行的函数]                     class MyProcess(multiprocessing.Process):                          def __init__(self):                             super(MyThread,self).__init__()                             # 其他初始化代码,如要传给函数值                          def run(self): pass                  2. 创建实例,开启显线程                     p = MyProcess()                     p.start()           # 会调用其run()方法

     3. 子进程和主进程的关系:        1> 主进程和子进程分别执行, 主线程会等待最后一个子进程执行完毕后, 关闭

        2> 子进程p.join()到主进程, 会阻塞主线程, 执行完该子进程后主进程继续执行, 最后主进程等待最后一个子进程执行完毕后, 关闭

        3> 在子进程开启之前, 申明该子进程为p.daemon = True守护进程, 最后主线程不会等待该子线程, 其他进程执行完就关闭主线程

     4. 进程的属性和方法:        1) 对象属性和方法: p.attr p.func()                 p.func():                      p.start()          # 启动进程                     p.join()           # 阻塞当前进程                     p.run()            # 在当前进程下调用该进程的run方法

                     p.isAlive()        # 判断进程是否被激活                     p.terminate()      # 结束进程             p.attr:                     p.name                # 线程名                     p.daemon = True/False # 是否为守护线程                     p.num/p.pid           # 进程号

        2) os方法:             os.getpid()                   # 获取当前进程号             os.getppid()                  # 获取当前进程父进程号‘‘‘  # 多进程: 使用, 见Mulit_Process.py

‘‘‘多进程通信:    1. 锁机制: Lock/ RLock/ Event/ Semaphore 同多线程    2. 进程队列: multiprocessing.Queue       同多线程    进程间资源不能共享, 因此在主线程中创建的Lock, RLock, Event, Semaphore, Queue, 要作为参数传入到子线程中

    3. 管道(Pipe): 通过管道两端的conn双向通信           用法:                 #1. conn1, conn2 = multiprocessing.Pipe()    //在主进程中创建               #2. 将conn1, conn2作为参数传入进程中               #3. 每一个conn有conn.send(), conn.recv()方法               #4. conn.recv()为空阻塞, conn.send()为空不阻塞

    4. 共享数据(Manager): 所有进程都可访问的数据块          用法:              #1. manager = multiprocessing.Manager()   // 主进程中创建              #2. 通过manager对象可创建公共的                     list, dict                            // 也需要传入                     Lock, RLock, Event, Semaphore, Queue  // 也需要传入              #3. 各个进程可访问这些数据集, 注意进程不安全的(除了Queue)要加锁

    5. 进程池(Pool):  与信号量类似,但没有加锁。               信号量是,可以开无限多的进程(线程),但某一时刻只允许num个进程(线程)修改数据               进程池是,从始至终只开num个进程‘‘‘  # 多进程通信: 锁机制, 进程队列, Pipe, Manager, Pool

原文地址:https://www.cnblogs.com/lancelotxly/p/10837806.html

时间: 2024-08-25 16:21:34

python并发编程(一):多线程,多进程的相关文章

Python并发编程04/多线程

目录 Python并发编程04/多线程 1.生产消费者模型 2.线程的理论知识 2.1什么是线程 2.2线程vs进程 2.3线程的应用 3.开启进程的两种方式 3.1第一种方式 3.2第一种方式 4.线程vs进程的代码对比 4.1开启速度对比 4.2对比pid 4.3同一个进程内线程共享内部数据 5.线程的其他方法 6.join与守护线程 6.1join 6.2守护线程 7.互斥锁 Python并发编程04/多线程 1.生产消费者模型 #编程思想,模型,设计模式,理论等等,都是交给你一种编程的方

python并发编程之多线程理论部分

阅读目录 一 什么是线程 二 线程的创建开销小 三 线程与进程的区别 四 为何要用多线程 五 多线程的应用举例 六 经典的线程模型(了解) 七 POSIX线程(了解) 八 在用户空间实现的线程(了解) 九 在内核空间实现的线程(了解) 十 用户级与内核级线程的对比(了解) 十一 混合实现(了解) 一 什么是线程 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 车间负责把资源整合到

python并发编程之多线程2------------死锁与递归锁,信号量等

一.死锁现象与递归锁 进程也是有死锁的 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用, 它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程, 如下就是死锁 1 死锁------------------- 2 from threading import Thread,Lock,RLock 3 import time 4 mutexA = Lock() 5 mutexB = Lock() 6

python并发编程之多线程

一,什么是线程 线程也被称为轻量进程计算机科学术语,指运行中的程序的调度单位. 线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程.线程不拥有系统资源,只有运行必须的一些数据结构:它与父进程的其它线程共享该进程所拥有的全部资源.线程可以创建和撤消线程,从而实现程序的并发执行.一般,线程具有就绪.阻塞和运行三种基本状态. 二,线程与进程的区别 1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 2) 线程的划分尺度小于进程,使得多线程程序的并发性高. 3) 另外,

python 并发编程之多线程

一.线程理论 1.什么是线程 多线程(即多个控制线程)的概念是,在一个进程中存在多个线程,多个线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源. 所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位. 2.进程与线程的区别 同一进程内的多个线程共享该进程内的地址资源 创建线程的开销要远小于创建进程的开销(创建一个进程,就是创建一个车间,涉及到申请空间,而且在该空间内建至少一条流水线,但创建线程,就只是在一个车间内造一

python并发编程之多线程基础知识点

1.线程理论知识 概念:指的是一条流水线的工作过程的总称,是一个抽象的概念,是CPU基本执行单位. 进程和线程之间的区别: 1. 进程仅仅是一个资源单位,其中包含程序运行所需的资源,而线程就相当于车间的流水线,负责执行具代码. 2. 每个进程至少包含一个线程,由操作系统自动创建,称之为主线程 3. 每个进程可以有任意数量的线程 4.创建进程的开销要比创建进程小得多 5. 同一进程的线程间数据是共享的 6.线程之间是平等的,没有子父级关系,同一进程下的各线程的PID相同 7. 创建线程的代码可以写

python并发编程中的多进程(代码实现)

一.multipricessing模块的介绍 python中的多线程无法利用多核优势,如果想要充分的使用多核CPU资源,在python中大部分情况下需要用多线程,python提供了multiprocessing模块 multiprocessing模块用来开启子进程,并在子进程中执行我们的任务(比如函数),该模块与多线程模块threading类的编程接口类似. multiprocessing模块的功能众多:支持子进程.通信和共享数据,执行不同形式的同步,提供了Process类,Queue类,Pip

python并发编程:多线程-线程理论

一 什么是线程 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程(流水线的工作需要电源,电源就相当于CPU),而一条流水线必须属于一个车间,一个车间的工作过程是一个进程,车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一条流水线. 所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是CPU上的执行单位. 多新村(即多个控制线程)的概念时:在一个进程中存在多个线程,多个线程共享该进程的地址空间,

python并发编程之多线程(实践篇)

一.threading模块介绍 官网链接:https://docs.python.org/3/library/threading.html?highlight=threading# 1.开启线程的两种方式 #直接调用 import threading import time def run(n): print('task',n) time.sleep(2) t1 = threading.Thread(target=run,args=('t1',)) t1.start() #继承式调用 mport

python并发编程:多线程-开启线程的两种方式

一 threading模块介绍 multiprocess模块完全模仿了threading模块的接口,二者在使用层面,有很大的相似性 二 开启线程的两种方式 方式一 from threading import Thread import time def sayhi(name): time.sleep(2) print("%s say hello" % name) if __name__ == '__main__': t = Thread(target=sayhi, args=('mik