• Python 进程,线程,协程


    本节内容

    1. 进程和线程

    2. python GIL全局解释锁
    3. 线程语法

    4. 进程语法

    1.进程与线程

    1.1 什么是进程

    程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。

    在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。

    在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。正是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。

    有了进程为什么还要线程?

    进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:

    • 进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。

    • 进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

    直白一点, 一个操作系统就像是一个工厂,工厂里面有很多个生产车间,不同的车间生产不同的产品,每个车间就相当于一个进程,且你的工厂又穷,供电不足,同一时间只能给一个车间供电,为了能让所有车间都能同时生产,你的工厂的电工只能给不同的车间分时供电,但是轮到你的qq车间时,发现只有一个干活的工人,结果生产效率极低,为了解决这个问题,应该怎么办呢?。。。。没错,你肯定想到了,就是多加几个工人,让几个人工人并行工作,这每个工人,就是线程!

    1.2 什么是线程

    线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务

    2.python GIL全局解释锁

    Python GIL(Global Interpreter Lock)  

    In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

    上面的核心意思就是,无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行。

    首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL

    3.线程

    线程的用法集合:

    • start  线程准备就绪,等待CPU调度
    • setName  为线程设置名称
    • getName  获取线程名称
    • setDaemon  设置为后台线程或前台线程(默认)
      •   如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止
      •   如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
    • join              逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
    • run              线程被cpu调度后自动执行线程对象的run方法

    3.1 threading模块

    线程有2种调用方式,如下

    直接调用

    import threading
    def task(arg):  # 定义每个线程要运行的函数
        print(arg)
    
    for i in range(5):
        t = threading.Thread(target=task,args=(i,))  # 生成一个线程实例
        t.start()  # 启动线程
        print(t.getName())  # 获取线程名
    print('end')
    

    继承调用

    import threading
    
    class Mythread(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.num = enumerate
        def run(self):# 定义每个线程要运行的函数
            print('running on number:%s'%self.name)
            
    if __name__ == '__main__':
        for i in range(10):
            t = Mythread(i)
            t.start()
    

    3.2 线程锁(Lock,RLock)

    由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁

    - 同一时刻允许一个线程执行操作。

    import threading
    import time
    
    def sayhi(num):
        lock.acquire()
        time.sleep(1)
        print('running %s'%num)
        lock.release()
    
    if __name__ == '__main__':
        lock = threading.Lock() # 普通的锁
        for i in range(10):
            t = threading.Thread(target=sayhi,args=(i,)) # 创建进程
            print(t.getName())
            t.start()
        print('主程序'.center(20,'-'))
    
    import threading
    import time
    
    def sayhi(num):
        lock.acquire()
        lock.acquire()
        time.sleep(1)
        print('running %s'%num)
        lock.release()
        lock.release()
    
    if __name__ == '__main__':
        lock = threading.RLock()  # 递归锁
        for i in range(10):
            t = threading.Thread(target=sayhi,args=(i,)) # 创建进程
            print(t.getName())
            t.start()
        print('主程序'.center(20,'-'))
    

    3.2 信号量(Semaphore)

    互斥锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。

    import threading
    import time
    
    def sayhi(num):
        lock.acquire()
        # lock.acquire()
        time.sleep(1)
        print('running %s'%num)
        lock.release()
        # lock.release()
    
    if __name__ == '__main__':
        lock = threading.BoundedSemaphore(2)  # 同时允许两个人操作数据
        for i in range(10):
            t = threading.Thread(target=sayhi,args=(i,)) # 创建进程
            print(t.getName())
            t.start()
        print('主程序'.center(20,'-'))
    

    3.3 事件(event)

    python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

    事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。

    • clear:将“Flag”设置为False
    • set:将“Flag”设置为True
    import threading
    
    def sayhi(num):
        event.wait()  # 锁住所有线程
        print('running :%s'%num)
    
    if __name__ == '__main__':
        event = threading.Event()
        for i in range(10):
            t = threading.Thread(target=sayhi,args=(i,))
            t.start()
        event.clear()
        string = input(':>>')
        if string == 'true':
            event.set()  # 打开锁,允许所有线程通过
    

    3.4 条件(Condition)

    使得线程等待,只有满足某条件时,才释放n个线程

    import threading
    import time
    lock = threading.Condition()  # 指定解锁的进程个数
    def take(arg):
        time.sleep(1)
        lock.acquire()
        # 锁住所有的线程
        lock.wait()
        print(arg)
        lock.release()
    for i in range(10):
        t = threading.Thread(target=take,args=(i,))
        t.start()
    
    while True:
        value = input('>>')
        lock.acquire()
        lock.notify(int(value))
        lock.release()
    

    3.5 Timer

    定时器,指定n秒后执行某操作

    import threading
    import time
    def sayhi(num):
        time.sleep(5)
        print('running :%s'%num)
    
    if __name__ == '__main__':
        for i in range(10):
            t = threading.Timer(1,sayhi,args=(i,))
            t.start()

    4 进程

    multiprocessing是python的多进程管理包,和threading.Thread类似。

    4.1 定义进程

    import multiprocessing  # 导入函数
    import time
    def foo(i): # 定义进程执行的方法
    time.sleep(2)
    print('say hi',i)

    if __name__ == '__main__':
    for i in range(10):
    p = multiprocessing.Process(target=foo, args=(i,)) # 创建进程
    p.start() # 执行进程
    # p.join() # 等待进程执行完毕

    进程的使用方法和线程的基本一致,可以参考上面说的,现在主要说说进程间通信。

    4.2 进程间通信

    在使用并发设计的时候最好尽可能的避免共享数据,尤其是在使用多进程的时候。 如果你真有需要 要共享数据, multiprocessing提供了两种方式。

    不同进程间内存是不共享的,要想实现两个进程间的数据交换,可以用以下方法:

    # 该例可以看出进程间的是不能进行数据交换的
    import multiprocessing
    import time
    li = []

    def sayhi(num):
    li.append(num)
    print('hi',num)

    if __name__ == '__main__':
    for i in range(3):
    p = multiprocessing.Process(target=sayhi,args=(i,))
    p.start()
    p.join()
    time.sleep(1)
    print(li)
    # 输出 # hi 0 # hi 1 # hi 2 # []

    第一种: 

    (1)multiprocessing,Array,Value  

    Value的使用参考http://www.cnblogs.com/aylin/p/5601969.html

    数据可以用Value或Array存储在一个共享内存地图里,如下:

    # 方法一,Array
    from multiprocessing import Process, Array
    # Array调用c语言底层,需要制定类型,列表元素必须统一。
    temp = Array('i', [11, 22, 33, 44]) 
    
    def Foo(i):
        temp[i] = 100 + i
        for item in temp:
            print(i, '----->', item)
    
    if __name__ == '__main__':
        for i in range(2):
            p = Process(target=Foo, args=(i,))
            p.start()
    

    第二种:

    (2)multiprocessing,Manager

    由Manager()返回的manager提供list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array类型的支持

    from multiprocessing import Process,Manager
    
    def task(num,li):  # 把列表传给函数
        li.append(num)
        print(li)
    if __name__ == '__main__':
        v = Manager().list()  # 通过Manager生成列表
        # v = Manager().dict()
        for i in range(10):
            p = Process(target=task,args=(i,v,))
            p.start()
            p.join()  # 串行执行
    

    线程池,进程池,还有协程下节讲。

    最新内容可以看我的blog: 40kuai
  • 相关阅读:
    java基础——DecimalFormat
    剑指——重建二叉树
    error error: illegal character: 'u3000'
    Android: Unhandled exception java.net.malformedurlexception 异常笔记
    Android获取系统时间
    java基础——hashCode笔记
    golang 红黑树
    golang 实现跳表skiplist
    快排
    堆排序
  • 原文地址:https://www.cnblogs.com/40kuai/p/6612651.html
Copyright © 2020-2023  润新知