zoukankan      html  css  js  c++  java
  • Python基础线程和协程

    线程:
      优点:共享内存,IO操作时,创造并发操作
      缺点:枪战资源
      线程不是越多越好,具体案例具体分析,请求上下文切换耗时
      IO密集型适用于线程,IO操作打开文件网络通讯类,不需要占用CPU,只是由CPU调度一下(不占用CPU)

    自定义进程和线程:注意python解释器自带了主进程和主线程,比如在代码文件里没有定义线程和进程,程序也能运行就是靠的解释器自带主进程的主线程执行的

      自定义进程:
        由主进程创建,子进程
      自定义线程:
        由主线程创建,子线程

    GIL全局解释器锁:

    GIL全局解释器锁在进程入口,控制着进程数量与CPU的相应

    threading线程模块

    线程是应用程序中工作的最小单元

    threading 模块建立在 _thread 模块之上。thread 模块以低级、原始的方式来处理和控制线程,而 threading 模块通过对 thread 进行二次封装,提供了更方便的 api 来处理线程。

    hread()创建线程对象【有参】

    使用方法:赋值变量 = 模块名称.Thread(target=事件函数,args=元祖类型事件函数的实际参数)  如函数多个参数,元祖里就是多个元素

    格式:t = threading.Thread(target=show, args=(i,))

    currentThread()获取当前线程【无参】

    使用方法:自定义变量 = threading模块名称.currentThread()

    格式:current_thread = threading.currentThread()

    start()激活线程【无参】

    使用方法:thread对象变量.start()

    格式:t.start()

    #!/usr/bin/env python
    # -*- coding:utf8 -*-
    import threading #导入线程模块
    import time #导入时间模块
    def show(arg): #定义函数
        time.sleep(3) #睡眠3秒
        print('线程'+str(arg)) #打印线程加循环次数
    for i in range(10): #定义一个10次循环
        t = threading.Thread(target=show, args=(i,)) #用threading模块的Thread类来创建子线程对象
        t.start() #激活子线程
    print("默认主线程等待子线程完成任务后,主线程停止")
    # 输出
    # 默认主线程等待子线程完成任务后,主线程停止
    # 线程0
    # 线程5
    # 线程8
    # 线程3
    # 线程6
    # 线程4
    # 线程1
    # 线程7
    # 线程2
    # 线程9

    上述代码创建了10个“前台”线程,然后控制器就交给了CPU,CPU根据指定算法进行调度,分片执行指令。

    from threading import Thread
    import time
    class Sayhi(Thread):
        def __init__(self,name):
            super().__init__()
            self.name=name
        def run(self):
            time.sleep(2)
            print('%s say hello' % self.name)
    
    
    if __name__ == '__main__':
        t = Sayhi('egon')
        t.start()
        print('主线程')

    在一个进程下开启多个线程与在一个进程下开启多个子进程的区别

    from threading import Thread
    from multiprocessing import Process
    import os
    
    def work():
        print('hello')
    
    if __name__ == '__main__':
        #在主进程下开启线程
        t=Thread(target=work)
        t.start()
        print('主线程/主进程')
        '''
        打印结果:
        hello
        主线程/主进程
        '''
    
        #在主进程下开启子进程
        t=Process(target=work)
        t.start()
        print('主线程/主进程')
        '''
        打印结果:
        主线程/主进程
        hello
        '''

    线程的join与setdaemon

    与进程的方法都是类似的,其实是multiprocessing模仿threading的接口

    join与setdaemon

    join()逐个执行每个线程,等待一个线程执行完毕后继续往下执行,该方法使得多线程变得无意义【有参可选】

    有参可选,参数为等待时间,秒为单位,如t.join(1) 就是一个线程不在是等待它执行完,而是只等待它1秒后继续下一个线程

    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=('egon',))
        t.setDaemon(True) #等待子线程完成
        t.start()
        t.join()
        print('主线程')
        print(t.is_alive())
    Thread实例对象的方法
    # isAlive(): 返回线程是否活动的。

      isAlive()判断线程是否为激活状态返回布尔值【无参】

      使用方法:thread对象变量.isAlive()

      格式:t.isAlive()

     # getName(): 返回线程名。
      getName()获取线程的名称【无参】
     使用方法:thread对象变量.getName()
     格式:t.getName()
      # setName(): 设置线程名。

      setName()设置线程的名称【有参】

      使用方法:thread对象变量.setName("要设置的线程名称")

      格式:t.setName("jhf")

      name获取或设置线程的名称【无参】

      使用方法:thread对象变量.name

      格式:t.name

      

      isDaemon()判断是否为守护线程,也就是主线程是否等待子线程执行完成后,才停止主线程,返回布尔值【无参】

      使用方法:thread对象变量.isDaemon()

      格式:t.isDaemon()

    threading模块提供的一些方法:
      # threading.currentThread(): 返回当前的线程变量。
      # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
      # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

    线程锁threading.RLock和threading.Lock

     我们使用线程对数据进行操作的时候,如果多个线程同时修改某个数据,可能会出现不可预料的结果,为了保证数据的准确性,引入了锁的概念。

    没有线程锁的情况列如:一个全局变量值为50,创建10条线程让每条线程累计减一,输出的结果是10个40,原因是10条线程同时减一就减去了10,所以打印出来就是10个40了

    未使用锁:

    #!/usr/bin/env python
    # -*- coding:utf8 -*-
    import threading #导入线程模块
    import time #导入时间模块
    globals_num = 50 #设置一个变量
    def Func(a): #定义一个函数
        global globals_num #将变量转换成全局变量,函数里可以调用
        globals_num -= 1 #全局变量减1
        time.sleep(1) #睡眠1秒
        print(globals_num,a) #打印全局变量减少后的结果,和函数传进来的值
    for i in range(10): #创建一个10次循环
        t = threading.Thread(target=Func,args=(i,)) #创建线程对象
        t.start() #激活线程
    # 输出  没有线程锁,线程之间抢占了数据资源
    # 40 5
    # 40 3
    # 40 6
    # 40 4
    # 40 0
    # 40 2
    # 40 1
    # 40 9
    # 40 8
    # 40 7
    根据上列情况可以看出,没有线程锁,线程之间抢占了数据资源
    线程锁就是将线程锁定,一个线程执行完毕后释放锁后在执行第二个线程

    RLock()定义线程锁对象

    使用方法:定义对象变量 = threading模块名称.RLock()

    格式:lock = threading.RLock()

    acquire()获得锁,将线程锁定,一个线程执行完毕释放锁后在执行第二个线程

    使用方法:线程锁对象变量.acquire()

    格式:lock.acquire()

    release()释放线程锁

    使用方法:线程锁对象变量.release()

    格式:lock.release()

    使用锁:

    #!/usr/bin/env python
    # -*- coding:utf8 -*-
    import threading #导入线程模块
    import time #导入时间模块
    globals_num = 50 #设置一个变量
    lock = threading.RLock()
    def Func(a): #定义一个函数
    
        lock.acquire()  # 获得锁,将线程锁定,一个线程执行完毕后在执行第二个线程
    
        global globals_num #将变量转换成全局变量,函数里可以调用
        globals_num -= 1 #全局变量减1
        time.sleep(1) #睡眠1秒
        print(globals_num,a) #打印全局变量减少后的结果,和函数传进来的值
    
        lock.release()  # 释放锁
    
    for i in range(10): #创建一个10次循环
        t = threading.Thread(target=Func,args=(i,)) #创建线程对象
        t.start() #激活线程
    # 输出  将线程锁定,一个线程执行完毕后在执行第二个线程
    # 49 0
    # 48 1
    # 47 2
    # 46 3
    # 45 4
    # 44 5
    # 43 6
    # 42 7
    # 41 8
    # 40 9

    threading.RLock和threading.Lock 的区别

    RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。 如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的琐。

    threading.Event事件对象

    Event()创建标识事件对象,全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞

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

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

    当线程执行的时候,如果flag为False,则线程会阻塞,当flag为True的时候,线程不会阻塞。它提供了本地和远程的并发性。

    Event事件对象的方法有

      wait([timeout]) : 堵塞线程,直到Event对象内部标识位被设为True或超时(如果提供了参数timeout)。
      set() :将标识位设为Ture
      clear() : 将标识位设为False。
      isSet() :判断标识位是否为Ture。

    #!/usr/bin/env python
    # -*- coding:utf8 -*-
    import threading
    def do(event):
        print('start')
        event.wait() #堵塞线程,直到Event对象内部标识位被设为True或超时(如果提供了参数timeout)
        print('execute')
    event_obj = threading.Event() #创建标识事件对象
    for i in range(10):
        t = threading.Thread(target=do, args=(event_obj,)) #创建线程对象
        t.start() #激活线程
    event_obj.clear() #将标识设置为False
    inp = input('input:')
    if inp == 'true':
        event_obj.set() #将标识设置为True
    # 输出
    # start
    # start
    # start
    # start
    # start
    # start
    # start
    # start
    # start
    # input:true
    # execute
    # execute
    # execute
    # execute
    # execute
    # execute
    # execute
    # execute
    # execute
    # execute

    threading.BoundedSemaphore信号对象

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

     BoundedSemaphore()创建信号对象【有参】

    使用方法:定义变量.threading.BoundedSemaphore(最大允许线程数)

    格式:semaphore  = threading.BoundedSemaphore(5)

    BoundedSemaphore信号对象的方法有

      acquire()获取信号
      release()释放信号

    from threading import Thread,Semaphore
    import time
    def work(id):
    with sem:
    time.sleep(2)
    print('%s say hello' %id)

    if __name__ == '__main__':
    sem=Semaphore(5) #最多以5个线程同时运行
    for i in range(20):
    t=Thread(target=work,args=(i,))
    t.start()

    queue模块

    Queue 就是对队列,它是线程安全的

    举例来说,我们去肯德基吃饭。厨房是给我们做饭的地方,前台负责把厨房做好的饭卖给顾客,顾客则去前台排队领取做好的饭。这里的前台就相当于我们的队列。

    这个模型也叫生产者-消费者模型。

    Queue()创建队列对象【有参】

    使用方法:定义变量 = queue.Queue(对列长度数) 0表示长度无限制

    格式:message = queue.Queue(10)

    Queue对象方法有:

      join()等到队列为空的时候,在执行别的操作【无参】

      qsize()返回队列的大小(不可靠),因为获取后有可能有新数据加入【无参】

      empty()清空队列里的所有数据

      full()检查队列是否为满,当队列满的时候,返回True,否则返回False(不可靠),因为获取后有可能有新数据加入【无参】

      put(放入对列的数据必选, block=True, timeout=None) 向队列里放入数据(生产)【有参】

        将数据放入对列尾部(生产),数据必须存在,可以参数block默认为True,表示当队列满时,会等待队列给出可用位置,为False时为非阻塞,此时如果队列已满,会引发queue.Full 异常。

        可选参数timeout,表示 会阻塞设置的时间,过后,如果队列无法给出放入item的位置,则引发 queue.Full 异常

      get(block=True, timeout=None)移除并返回队列头部的一个值(消费)【有参】

         可选参数block默认为True,表示获取值的时候,如果队列为空,则阻塞,为False时,不阻塞,若此时队列为空,则引发 queue.Empty异常。

        可选参数timeout,表示会阻塞设置的时候,过后,如果队列为空,则引发Empty异常。

      put_nowait(放入对列的数据必选)向队列里放入数据(生产)【有参】,如果队列满时不阻塞,不等待队列给出可用位置,引发 queue.Full 异常

      get_nowait()移除并返回队列头部的一个值(消费)【无参】,如果队列空时不阻塞,引发 queue.Full 异常

    class queue.Queue(maxsize=0) #先进先出

    import queue
    
    q=queue.Queue()
    q.put('first')
    q.put('second')
    q.put('third')
    
    print(q.get())
    print(q.get())
    print(q.get())
    '''
    结果(先进先出):
    first
    second
    third
    '''

    class queue.LifoQueue(maxsize=0) #last in fisrt out 

    import queue
    
    q=queue.LifoQueue()
    q.put('first')
    q.put('second')
    q.put('third')
    
    print(q.get())
    print(q.get())
    print(q.get())
    '''
    结果(后进先出):
    third
    second
    first
    '''

    class queue.PriorityQueue(maxsize=0) #存储数据时可设置优先级的队列

    import queue
    
    q=queue.PriorityQueue()
    #put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
    q.put((20,'a'))
    q.put((10,'b'))
    q.put((30,'c'))
    
    print(q.get())
    print(q.get())
    print(q.get())
    '''
    结果(数字越小优先级越高,优先级高的优先出队):
    (10, 'b')
    (20, 'a')
    (30, 'c')
    '''

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

    from threading import Timer
     
     
    def hello():
        print("hello, world")
     
    t = Timer(1, hello)
    t.start()  # after 1 seconds, "hello, world" will be printed
    
    
  • 相关阅读:
    Java的数组的作业11月06日
    e课表项目第二次冲刺周期第九天
    e课表项目第二次冲刺周期第八天
    e课表项目第二次冲刺周期第七天
    e课表项目第二次冲刺周期第六天
    e课表项目第二次冲刺周期第五天
    e课表项目第二次冲刺周期第四天
    e课表项目第二次冲刺周期第三天
    e课表项目第二次冲刺周期第二天
    e课表项目第二次冲刺周期第一天
  • 原文地址:https://www.cnblogs.com/zjltt/p/7121864.html
Copyright © 2011-2022 走看看