zoukankan      html  css  js  c++  java
  • python 线程

    一颗cpu同一时刻只能有一个线程执行。在cpython解释器中,同一时刻只能让同一个进程中只有一个线程进入解释器

    GIL是内核级别的锁:为了保护内核,被多线程干扰

    线程锁是用户级别的锁:为了保护程序里面的数据

    进程与线程

      线程:

        是最小的执行单位,是一堆指令的集合。cpu会记录上下文关系

        数据之间是共享的

        一个进程至少有一个线程

      进程:

        进程是不修改数据的。

        一堆资源(线程、cpu、内存等)的管理的集合

        进程之间的数据是不共享的。

    GIL:

      在cpython解释器中,同一时刻只能让同一个进程中只有一个线程进入解释器。因为同一进程中的线程是共享数据的

      在开发这么语言的时候,只有一颗cpu,为了保护在多线程保护数据一致性,所以开发了GIL。去锁有大咖尝试做过,但是效率更低

           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.)

    计算密集型与I/O密集型

    io密集型:

      线程在遇到I/0(sock的收发,文件的读写,sleep等)的时候,会自动进行切换到另外一个线程计算。就算只有一颗cpu的情况下,也大量节省了时间。

      如下代码,多线程会比串行节约一倍的时间

    import time
    import threading
    def foo1():
        time.sleep(8)
        print("1")
    def foo2():
        time.sleep(8)
        print("2")
    
    start=time.time()
    # foo1()
    # foo2()
    a=threading.Thread(target=foo1)
    b=threading.Thread(target=foo2)
    a.start()
    b.start()
    a.join()
    b.join()
    end=time.time()
    print("run time:",end-start)
    View Code

    计算密集型:

       下列代码,就是io密集型的表现,使用串行算法与 多线程算法 两者的时间几乎没区别(在python2.7和3.4中,多线程io因为不断切换线程,时间比串行慢了一半。3.5已经优化) python中因为GIL的存在,线程只能在一个cpu中计算。对应计算密集型多线程 不适用

    import time
    import threading
    def add(n):
        sum = 0
        for i in range(n):
            sum += i
        print(sum)
    start = time.time()
    # add(100000000)
    # add(200000000)
    a=threading.Thread(target=add,args=(100000000,))
    b=threading.Thread(target=add,args=(200000000,))
    a.start()
    b.start()
    a.join()
    b.join()
    end = time.time()
    
    print("time:",start-end)
    View Code

      解决同一个进程中的多线程在同一时刻只能有一个进入cpu的问题,可以利用多协程与携程 等等解决方法。

    线程的另外一种调用方式:

    import threading
    import time
     
     
    class MyThread(threading.Thread):
        def __init__(self,num):
            threading.Thread.__init__(self)
            self.num = num
     
        def run(self):#定义每个线程要运行的函数
     
            print("running on number:%s" %self.num)
     
            time.sleep(3)
     
    if __name__ == '__main__':
     
        t1 = MyThread(1)
        t2 = MyThread(2)
        t1.start()
        t2.start()
    View Code

    jion方法:

      阻塞线程,等待线程执行完毕。  线程可以通过列表装载。

    setdeamon:

      守护进程,主进程死了,下面的线程全挂、

    线程锁

    是为了解决线程之间 操作共享数据的问题

    线程不安全:

      如下代码:线程之间共享数据,假如第一次一个线程拿到全局变量,进行计算的时候,到了cpu的执行时间,不会进行最后一步重新赋值全局变量。

      但是第二次线程拿到全局变量,执行完毕,赋值给全局变量。两次的执行效果都是一样。

    import threading,time
    def add():
        global num
        temp = num
        temp -= 1
        time.sleep(0.001)
        num = temp
    num = 200
    thread_list=[]
    for i in range(100):
        t = threading.Thread(target=add,args=())
        t.start()
        thread_list.append(t)
    for t in thread_list:
        t.join()
    print(num)
    View Code

          解决方案:线程锁,就是加在线程上,告诉解释器,我必须执行完,你才能给我进行切换!

     acquire 与 release 之间变成了串行,就算遇到I/0也会被锁住

    但是在该线程 锁之外的东西(如下print(ok))还是可以进行cpu切换的,
    这就是和join的区别。 join是把线程所有的部分都变成了串行
    import threading,time
    def add():
        global num
        #!!acquire 与 release 之间变成了串行,就算遇到I/0也会被锁住
        # 但是在该线程 锁之外的东西(如下print(ok))还是可以进行cpu切换的,
        # 这就是和join的区别。 join是把线程所有的部分都变成了串行
        print("ok")
        r.acquire()
        temp = num
        temp -= 1
        time.sleep(0.001)
        num = temp
        r.release()
    num = 200
    thread_list=[]
    r = threading.Lock()
    for i in range(100):
        t = threading.Thread(target=add,args=())
        t.start()
        thread_list.append(t)
    for t in thread_list:
        t.join()
    print(num)
    View Code

    GIL 是 一个进程的所有线程,同一时刻只能一个进入cpu

    线程锁 是保证 一个进程中,多线程操作共享数据的时候,为了避免到时 切换cpu而操作数据的不安全。

      递归锁:普通的锁 只能被使用一次, 相互等待锁的时候,就会被锁死。(如下代码)递归锁 维护了 一个 计数器与对应列表。

    import threading,time
    
    class myThread(threading.Thread):
        def doA(self):
            lockA.acquire()
            print(self.name,"gotlockA",time.ctime())
            time.sleep(3)
            lockB.acquire()
            print(self.name,"gotlockB",time.ctime())
            lockB.release()
            lockA.release()
    
        def doB(self):
            lockB.acquire()
            print(self.name,"gotlockB",time.ctime())
            time.sleep(2)
            lockA.acquire()
            print(self.name,"gotlockA",time.ctime())
            lockA.release()
            lockB.release()
        def run(self):
            self.doA()
            self.doB()
    if __name__=="__main__":
    
        lockA=threading.Lock()
        lockB=threading.Lock()
        threads=[]
        for i in range(5):
            threads.append(myThread())
        for t in threads:
            t.start()
        for t in threads:
            t.join()#等待线程结束,后面再讲。
    View Code

       递归锁在类中包含 账户 转账、取现的时候,每个方法都加锁,同时一个方法调用前面函数,就需要用到递归锁了

    信号量锁:

    同一时刻只有三个链接并发。

    r = threading.BoundedSemaphore(3)
    r.acqure()
    r.release()

    条件同步锁

         有一类线程需要满足条件之后才能够继续执行,Python提供了threading.Condition 对象用于条件变量线程的支持,

        它除了能提供RLock()或Lock()的方法外,还提供了 wait()、notify()、notifyAll()方法。

          lock_con=threading.Condition([Lock/Rlock]): 锁是可选选项,不传人锁,对象自动创建一个RLock()。

    wait():条件不满足时调用,线程会释放锁并进入等待阻塞;等到了notify通知时候,会从acquire从新执行
    notify():条件创造后调用,通知等待池激活一个线程;
    notifyAll():条件创造后调用,通知等待池激活所有线程。

    enevt同步

      这个不是锁了,但是效果类似条件同步锁

      创建的时候,默认是Flase        flag=thread.Event()

    event.isSet():返回event的状态值;
    
    event.wait():如果 event.isSet()==False将阻塞线程;当set的时候,立马执行
    
    event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
    
    event.clear():恢复event的状态值为False。
    import threading,time
    import random
    def light():
        if not event.isSet():
            event.set() #wait就不阻塞 #绿灯状态
        count = 0
        while True:
            if count < 10:
                print('33[42;1m--green light on---33[0m')
            elif count <13:
                print('33[43;1m--yellow light on---33[0m')
            elif count <20:
                if event.isSet():
                    event.clear()
                print('33[41;1m--red light on---33[0m')
            else:
                count = 0
                event.set() #打开绿灯
            time.sleep(1)
            count +=1
    def car(n):
        while 1:
            time.sleep(random.randrange(10))
            if  event.isSet(): #绿灯
                print("car [%s] is running.." % n)
            else:
                print("car [%s] is waiting for the red light.." %n)
    if __name__ == '__main__':
        event = threading.Event()
        Light = threading.Thread(target=light)
        Light.start()
        for i in range(3):
            t = threading.Thread(target=car,args=(i,))
            t.start()
    View Code

    队列:今后常用的线程之间通信的利器

         在单线程这个根本没啥用,在多线程中,使用列表 线程不安全(多个线程 取或者放会覆盖)。quqe自己默认有个锁,保证了线程的安全。

    创建一个“队列”对象
    import Queue
    q = Queue.Queue(maxsize = 10)
    Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。
    
    将一个值放入队列中
    q.put(10)
    调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为
    1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。
    
    将一个值从队列中取出
    q.get()
    调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。
    
    Python Queue模块有三种队列及构造函数:
    1、Python Queue模块的FIFO队列先进先出。  class queue.Queue(maxsize)
    2、LIFO类似于堆,即先进后出。             class queue.LifoQueue(maxsize)
    3、还有一种是优先级队列级别越低越先出来。   class queue.PriorityQueue(maxsize)
    
    此包中的常用方法(q = Queue.Queue()):
    q.qsize() 返回队列的大小
    q.empty() 如果队列为空,返回True,反之False
    q.full() 如果队列满了,返回True,反之False
    q.full 与 maxsize 大小对应
    q.get([block[, timeout]]) 获取队列,timeout等待时间
    q.get_nowait() 相当q.get(False)
    非阻塞 q.put(item) 写入队列,timeout等待时间
    q.put_nowait(item) 相当q.put(item, False)
    q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
    q.join() 实际上意味着等到队列为空,再执行别的操作

    实例1:

    import threading,queue
    from time import sleep
    from random import randint
    class Production(threading.Thread):
        def run(self):
            while True:
                r=randint(0,100)
                q.put(r)
                print("生产出来%s号包子"%r)
                sleep(1)
    class Proces(threading.Thread):
        def run(self):
            while True:
                re=q.get()
                print("吃掉%s号包子"%re)
    if __name__=="__main__":
        q=queue.Queue(10)
        threads=[Production(),Production(),Production(),Proces()]
        for t in threads:
            t.start()
    View Code

    实例2:

    import time,random
    import queue,threading
    q = queue.Queue()
    def Producer(name):
      count = 0
      while count <20:
        time.sleep(random.randrange(3))
        q.put(count)
        print('Producer %s has produced %s baozi..' %(name, count))
        count +=1
    def Consumer(name):
      count = 0
      while count <20:
        time.sleep(random.randrange(4))
        if not q.empty():
            data = q.get()
            print(data)
            print('33[32;1mConsumer %s has eat %s baozi...33[0m' %(name, data))
        else:
            print("-----no baozi anymore----")
        count +=1
    p1 = threading.Thread(target=Producer, args=('A',))
    c1 = threading.Thread(target=Consumer, args=('B',))
    p1.start()
    c1.start()
    View Code

    实例3:

    #实现一个线程不断生成一个随机数到一个队列中(考虑使用Queue这个模块)
    # 实现一个线程从上面的队列里面不断的取出奇数
    # 实现另外一个线程从上面的队列里面不断取出偶数
    
    import random,threading,time
    from queue import Queue
    #Producer thread
    class Producer(threading.Thread):
      def __init__(self, t_name, queue):
        threading.Thread.__init__(self,name=t_name)
        self.data=queue
      def run(self):
        for i in range(10):  #随机产生10个数字 ,可以修改为任意大小
          randomnum=random.randint(1,99)
          print ("%s: %s is producing %d to the queue!" % (time.ctime(), self.getName(), randomnum))
          self.data.put(randomnum) #将数据依次存入队列
          time.sleep(1)
        print ("%s: %s finished!" %(time.ctime(), self.getName()))
    
    #Consumer thread
    class Consumer_even(threading.Thread):
      def __init__(self,t_name,queue):
        threading.Thread.__init__(self,name=t_name)
        self.data=queue
      def run(self):
        while 1:
          try:
            val_even = self.data.get(1,5) #get(self, block=True, timeout=None) ,1就是阻塞等待,5是超时5秒
            if val_even%2==0:
              print ("%s: %s is consuming. %d in the queue is consumed!" % (time.ctime(),self.getName(),val_even))
              time.sleep(2)
            else:
              self.data.put(val_even)
              time.sleep(2)
          except:   #等待输入,超过5秒 就报异常
            print ("%s: %s finished!" %(time.ctime(),self.getName()))
            break
    class Consumer_odd(threading.Thread):
      def __init__(self,t_name,queue):
        threading.Thread.__init__(self, name=t_name)
        self.data=queue
      def run(self):
        while 1:
          try:
            val_odd = self.data.get(1,5)
            if val_odd%2!=0:
              print ("%s: %s is consuming. %d in the queue is consumed!" % (time.ctime(), self.getName(), val_odd))
              time.sleep(2)
            else:
              self.data.put(val_odd)
              time.sleep(2)
          except:
            print ("%s: %s finished!" % (time.ctime(), self.getName()))
            break
    #Main thread
    def main():
      queue = Queue()
      producer = Producer('Pro.', queue)
      consumer_even = Consumer_even('Con_even.', queue)
      consumer_odd = Consumer_odd('Con_odd.',queue)
      producer.start()
      consumer_even.start()
      consumer_odd.start()
      producer.join()
      consumer_even.join()
      consumer_odd.join()
      print ('All threads terminate!')
    
    if __name__ == '__main__':
      main()
    View Code

    线程不安全实例:

    import threading,time
    
    li=[1,2,3,4,5]
    
    def pri():
        while li:
            a=li[-1]
            print(a)
            time.sleep(1)
            try:
                li.remove(a)
            except:
                print('----',a)
    
    t1=threading.Thread(target=pri,args=())
    t1.start()
    t2=threading.Thread(target=pri,args=())
    t2.start()
    View Code
  • 相关阅读:
    react深入学习(资料,案例)
    match.exec深入学习
    实用插件表格行列隐藏显示
    下拉选项插件的实现
    表格操作eventTable
    [CentOS7] 挂载iso镜像文件到/media目录下
    [CentOS7] 设置开机启动方式(图形界面或命令行)
    [CentOS7] 磁盘分区(gdisk, fdisk)
    [CentOS7] minimal安装后 出现 没有ifconfig 无法ping 无法yum could not retrieve mirrorlist http://mirrorlist.centos.org/
    [C++]C,C++中使用可变参数
  • 原文地址:https://www.cnblogs.com/louhui/p/7944583.html
Copyright © 2011-2022 走看看