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)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
    努力有用的话,还要天才做什么呢?
  • 相关阅读:
    signal, sigaction,信号集合操作
    how to create view (windows)
    设计模式-单例模式
    clearcase command (windows 常用的几个)
    struts2提交文件时,出现页面重置又无法返回input现象,我没有解决这个问题
    报错如HTTP Status 404
    srm开发(基于ssh)(3)
    报错HTTP Status 500
    编写程序时候莫名出现<property name="dialect">org.hibernate.dialect.FirebirdDialect</property>
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [bean.xml]: Invocation of init method failed; nested exception is
  • 原文地址:https://www.cnblogs.com/crazy-xf/p/9774503.html
Copyright © 2011-2022 走看看