zoukankan      html  css  js  c++  java
  • python线程、进程、协程

    一、线程

    有时被称为轻量级进程,是程序执行流的最小单元。一个标准的线程由线程ID、当前指令指针(PC)、寄存器集合和堆栈组成。是被系统独立调度和和分派的基本单位。线程不独立拥有系统资源,但是它可以与同属一个进程的其他线程共享该进程资源。同一个进程的多线程之间可以并发执行。
    线程也有就绪、阻塞和运行三种基本状态。
    就绪状态是指线程具备运行的条件,逻辑上可以运行,在等待处理机;
    运行状态是指线程占有处理机正在运行;
    阻塞状态是指线程在等待一个事件(如信号量),逻辑上不可执行。
    每一个应用程序中都至少一个线程和一个进程。在单个程序中同时运行多个线程完成不同的工作,叫做多线程。
    

    普通的多线程:

    import threading
    import time
    
    def foo(num):
        time.sleep(1)
        print("now is number {}".format(num))
    
    
    for i in range(10):
        t = threading.Thread(target=foo, args=(i,))
        t.start()
    print("main thread")
    ----------------------------------------------------------------------------
    start       线程准备就绪,等待CPU调度
    setName     为线程设置名称
    getName     获取线程的名称
    setDaemon   设置为守护线程。即主线程是否等待子线程执行完毕。
    join        让程序变为串行的
    

    自定义线程类:

    import threading
    class MyThreading(threading.Thread):
        def __init__(self, *args, **kwargs):
            super(MyThreading, self).__init__(*args, **kwargs)
        
        def run(self):
            print("now is number {}".format(self.num))
    
    for i in range(10):
        t = MyThreading(num=i)
        t.start()
    

    锁的概念是:限制某一时刻只有一个线程能访问某个指定的数据

    锁一共有五种:

    1.Lock 普通锁,不支持嵌套
    2.RLock 普通锁,支持嵌套
    3.BoundedSemaphore 信号量
    4.Event 事件
    5.Condition 条件
    

    1. 普通锁

    普通锁也叫互斥锁,是独占的,同一时刻只有一个线程被执行。
    包括Lock和RLock
    
    import time
    import threading
    
    num = 10
    def foo(lock):
        global num
        lock.acquire()
        num -= 1
        time.sleep(1)
        print(num)
        lock.release()
    
    lock = threading.RLock()
    for i in range(10):
        t = threading.Thread(target=foo, args=(lock,))
        t.start()
    

    2. 信号量

    类名:BoundedSemaphore
    这种锁允许一定数量的线程同时更改数据,他不是互斥锁。比如地铁安检,排队人很多,工作人员只允许一定数量的人进入安检区,其它的人继续排队。
    
    import threading
    import time
    
    def foo(i, semaphore):
        semaphore.acquire() # 上锁
        print(i)
        time.sleep(1)
        semaphore.release() #释放锁
    
    semaphore = threading.BoundedSemaphore(5) #最多允许5个线程同时执行
    for i in range(10):
        t = threading.Thread(target=foo, args=(i, semaphore))
        t.start()
    

    3. 事件

    类名:Event
    主要提供了三个方法,
    event = threading.Event()
    event.set()
    event.clear()
    event.wait()
    事件机制:全局定义了一个Flag,如果Flag为False的话,那么当程序执行wait时就会阻塞,如果Flag为True,执行wait时就不再阻塞。类似交通红绿灯
    event.clear()  ---将Flag设置False
    event.set()  ---将Flag设置True
    
    import threading
    import time
    
    '''
            线程的event
    '''
    event = threading.Event()
    
    
    def lighter():
        counter = 0
        event.set()    # 设置标志位,表示现在是绿灯,可以通行
        while True:
            if counter >= 5 and counter < 10:
                event.clear()    #清除标志位,表示红灯亮起,禁止通行
                print("33[41;1m now is red light,please waiting......33[0m")
            elif counter > 10:
                event.set()      # 重设标志位,并且使计数器重设成0
                counter = 0
            else:
                print("33[42;1m now is green light,start running......33[0m")
            time.sleep(1)
            counter += 1
    
    
    def car(name):
        while True:
            if event.is_set():
                print("[%s] is running" % name)
                time.sleep(1)
            else:
                print("now ,Please waiting to green light [%s]" % name)
                event.wait()
                print("becoming green,you can running [%s]" % name)
    
    
    light = threading.Thread(target=lighter,)
    light.start()
    
    cars = threading.Thread(target=car, args=("Tesla",))
    cars.start()
    

    4. 条件

    类名:Condition
    该机制会使线程等待,只有满足某条件时,才释放n个线程
    
    import threading, time
    
    def run(n):
        con.acquire()
        con.wait()
        print("run the thread: %s" %n)
        con.release()
    
    if __name__ == '__main__':
    
        con = threading.Condition()
        for i in range(10):
            t = threading.Thread(target=run, args=(i,))
            t.start()
    
        while True:
            inp = input('>>>')
            if inp == "q":
                break
            # 下面这三行是固定语法
            con.acquire()
            con.notify(int(inp))  # 这个方法接收一个整数,表示让多少个线程通过
            con.release()
            time.sleep(0.5)
    

    二、队列

    队列是一种先进先出的数据结构,与之对应的是堆栈这种后进先出的结构。python内置了一个queue模块,包含队列如下:
    1.queue.Queue(): 先进先出队列
    2.queue.LifoQueue(): 后进先出队列
    3.queue.PriorityQueue(): 优先级队列
    4.queue.deque(): 双向队列
    
    
    (1)先进先出队列--queue.Queue(5) 
    import queue
    
    q = queue.Queue(5)  # 5是指定队列的大小,为空的话队列无限大
    for i in range(5):
        q.put(i)
        print(q.get())
    
    队列的一些方法:
    q.qsize()  ---获取当前队列中元素的个数,也就是当前队列的大小
    q.empty()  ---判断当前队列是否为空,返回True或False
    q.full()  ---判断当前队列是否已满,返回True或False
    q.put(self, block=False, timeout=None)  ---往队列里放入一个元素,默认是不阻塞和无时间限制的。
    q.get(self, block=False, timeout=None)  ---在队列里取一个元素,参数和放入是一样的。
    q.join()  ---阻塞进程,直到所有任务完成,需配合task_done方法使用。
    q.task_done()  ---表示某个任务完成。每一条get语句后需要一条task_done
    
    (2)后进先出队列--queue.LifoQueue()
    
    import queue
    q = queue.LifoQueue()
    q.put(123)
    q.put(456)
    print(q.get())
    >>>>>>>>>>>>>>获得456
    
    (3)优先级队列--queue.PriorityQueue()
    带有权重的队列,每个元素都是一个元组,前面的数字表示它的优先级,数字越小优先级越高,同样的优先级先进先出。
    
    import queue
    
    q = queue.PriorityQueue()
    
    q.put((1, 'alex'))
    q.put((12, 'alice'))
    q.put((1, 'jack'))
    q.put((13, 'eric'))
    print(q.get())
    print(q.get())
    print(q.get())
    >>>>结果是(1, 'alex')  (1, 'jack') (12, 'alice')
    获取其中的元素就是和列表一样,用索引来获取
    
    (4)双向队列--queue.deque()
    import queue
    q = queue.deque()
    q.append(123)
    q.append(333)
    q.appendleft(456)
    q.pop()
    q.popleft()
    

    三、进程

    在python中multiprocessing模块提供了Process类,实现进程相关的功能。但是他是基于fork机制的,因此不被windows平台支持,想要在windows中运行,必须使用if __name__ == '__main__:的方式,显然这只能用于调试和学习,不能用于实际环境。
    (PS:在这里我必须吐槽一下python的包、模块和类的组织结构。在multiprocess中你既可以import大写的Process,也可以import小写的process,这两者是完全不同的东西。这种情况在python中很多,新手容易傻傻分不清。)
    一个简单的进程的栗子:
    from multiprocessing import Process
    
    def foo(i):
        print("This is Process ", i)
    
    if __name__ == '__main__':
        for i in range(5):
            p = Process(target=foo, args=(i,))
            p.start()
    用法和线程其实差不多。
    (3.1)进程的数据共享
    from multiprocessing import Process
    
    temp_list = []
    def foo(i):
        temp_list.append(i)
        print("This is Process {},list is :{}".format(i, temp_list))
    
    
    if __name__ == '__main__':
        for i in range(5):
            p = Process(target=foo, args=(i,))
            p.start()
    结果是:
    

    可以发现进程间的数据是相互独立的,在各个进程中只有自己的数据,完全不发共享。若要进程间共享资源可以用queues/Array/Manager这三个multiprocess模块提供的类。
    
    (3.2)使用Array共享数据
        
    
    from multiprocessing import Process
    from multiprocessing import Array
    
    def Foo(i,temp):
        temp[0] += 100
        for item in temp:
            print(i,'----->',item)
    
    if __name__ == '__main__':
        temp = Array('i', [11, 22, 33, 44])
        for i in range(2):
            p = Process(target=Foo, args=(i,temp))
            p.start()
    

    每一次的数据都是在上一次的基础上进行叠加
    
    (3.3)使用Manager共享数据
    
    from multiprocessing import Process,Manager
    
    def Foo(i,dic):
        dic[i] = 100+i
        print(dic.values())
    
    if __name__ == '__main__':
        manage = Manager()
        dic = manage.dict()
        for i in range(10):
            p = Process(target=Foo, args=(i,dic))
            p.start()
            p.join()
    

    Manager比Array要好用一点,因为它可以同时保存多种类型的数据格式      
    

    四、协程

    线程和进程的操作是由程序触发系统接口,最后的执行者是系统,它本质上是操作系统提供的功能。
    而协程的操作则是程序员指定的,在python中通过yield,人为的实现并发处理。
    协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时。协程,则只使用一个线程,分解一个线程成为多个“微线程”,在一个线程中规定某个代码块的执行顺序。
    协程的适用场景:当程序中存在大量不需要CPU的操作时(IO)
    
    栗子:
    from gevent import monkey; 
    import gevent
    import requests
    
    monkey.patch_all()
    def f(url):
        print('GET: %s' % url)
        resp = requests.get(url)
        data = resp.text
        print('%d bytes received from %s.' % (len(data), url))
    
    gevent.joinall([
            gevent.spawn(f, 'https://www.python.org/'),
            gevent.spawn(f, 'https://www.yahoo.com/'),
            gevent.spawn(f, 'https://github.com/'),
    ])
    

    进程、线程和协程的区别

    python中进程和线程的最大区别在于稳定性和效率问题。
    多进程之间互不影响,一个崩溃了不影响其他进程,稳定性较高
    多线程由于在同一个进程里,一个线程崩溃整个进程也会崩溃。
    
    多进程对系统资源开销大,多线程对系统资源开销小,这方面多线程占一点优势。
    
    协程最大的优势是高执行效率,因为子程序切换不是线程切换,没有切换线程的开销,不需要多线程锁。
    
    区别:
    (1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
    
    (2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
    
    (3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
    
    (4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
    努力有用的话,还要天才做什么呢?
  • 相关阅读:
    Spring MVC Ajax 嵌套表单数据的提交
    Spring MVC 过滤静态资源访问
    Spring MVC 页面跳转时传递参数
    IDEA Maven 三层架构 2、运行 springMVC
    IDEA Maven 三层架构 1、基本的Archetype 搭建
    EasyUI DataGrid 基于 Ajax 自定义取值(loadData)
    Spring MVC Ajax 复杂参数的批量传递
    Mybatis Sql片段的应用
    在 Tomcat 8 部署多端口项目
    自动升级的设计思路与实现
  • 原文地址:https://www.cnblogs.com/crazy-xf/p/9774503.html
Copyright © 2011-2022 走看看