• 多线程(threading module)


    一、线程与进程

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

    进程定义:An executing instance of a program is called a process.(程序的执行实例称为进程。)

    线程与进程的区别:

      1. 线程共享创建它的进程的地址空间; 进程有自己的地址空间。
      2. 线程可以直接访问其进程的数据段; 进程拥有自己父进程数据段的副本。
      3. 线程可以直接与其进程的其他线程通信; 进程必须使用进程间通信来与兄弟进程通信。
      4. 新线程很容易创建; 新流程需要复制父流程。
      5. 线程可以对同一进程的线程进行相当大的控制; 进程只能控制子进程。
      6. 对主线程的更改(取消,优先级更改等)可能会影响进程的其他线程的行为; 对父进程的更改不会影响子进程。

    二、Python GIL(Global Interpreter Lock)

      --> 全局解释器锁 :在同一时刻,只能有一个线程进入解释器。

    三、threading 模块

    3.1 线程的2种调用方式

    直接调用

     1 import threading
     2 import time
     3  
     4 def sayhi(num): #定义每个线程要运行的函数
     5  
     6     print("running on number:%s" %num)
     7  
     8     time.sleep(3)
     9  
    10 if __name__ == '__main__':
    11  
    12     t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例
    13     t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例
    14  
    15     t1.start() #启动线程
    16     t2.start() #启动另一个线程
    17  
    18     print(t1.getName()) #获取线程名
    19     print(t2.getName())
    View Code

    继承式调用

     1 import threading
     2 import time
     3  
     4  
     5 class MyThread(threading.Thread):
     6     def __init__(self,num):
     7         threading.Thread.__init__(self)
     8         self.num = num
     9  
    10     def run(self):#定义每个线程要运行的函数
    11  
    12         print("running on number:%s" %self.num)
    13  
    14         time.sleep(3)
    15  
    16 if __name__ == '__main__':
    17  
    18     t1 = MyThread(1)
    19     t2 = MyThread(2)
    20     t1.start()
    21     t2.start()
    View Code

    3.2 常用方法(Join/Daemon)

      join() --> 在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

      setDaemon(True) -->  将线程声明为守护线程,必须在start() 方法调用之前设置。当守护线程运行时间超过主线程时,守护线程将随主线程结束而结束。

    其他方法:

     1 threading 模块提供的其他方法:
     2 # threading.currentThread(): 返回当前的线程变量。
     3 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
     4 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
     5 # 除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
     6 # run(): 用以表示线程活动的方法。
     7 # start():启动线程活动。
     8 # join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
     9 # isAlive(): 返回线程是否活动的。
    10 # getName(): 返回线程名。
    11 # setName(): 设置线程名。
    Method

    3.3 同步锁(Lock)

      r = threading.Lock()  r.acquire() --> 加锁  r.release() --> 解锁

     1 import time
     2 import threading
     3 
     4 def addNum():
     5     global num #在每个线程中都获取这个全局变量
     6     # num-=1
     7     lock.acquire()
     8     temp=num
     9     print('--get num:',num )
    10     #time.sleep(0.1)
    11     num =temp-1 #对此公共变量进行-1操作
    12     lock.release()
    13 
    14 num = 100  #设定一个共享变量
    15 thread_list = []
    16 lock=threading.Lock()
    17 
    18 for i in range(100):
    19     t = threading.Thread(target=addNum)
    20     t.start()
    21     thread_list.append(t)
    22 
    23 for t in thread_list: #等待所有线程执行完毕
    24     t.join()
    25 
    26 print('final num:', num )
    View Code

    3.4 线程死锁和递归锁

      在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。下面是一个死锁的例子:

     1 import threading,time
     2 
     3 class myThread(threading.Thread):
     4     def doA(self):
     5         lockA.acquire()
     6         print(self.name,"gotlockA",time.ctime())
     7         time.sleep(3)
     8         lockB.acquire()
     9         print(self.name,"gotlockB",time.ctime())
    10         lockB.release()
    11         lockA.release()
    12 
    13     def doB(self):
    14         lockB.acquire()
    15         print(self.name,"gotlockB",time.ctime())
    16         time.sleep(2)
    17         lockA.acquire()
    18         print(self.name,"gotlockA",time.ctime())
    19         lockA.release()
    20         lockB.release()
    21     def run(self):
    22         self.doA()
    23         self.doB()
    24 if __name__=="__main__":
    25 
    26     lockA=threading.Lock()
    27     lockB=threading.Lock()
    28     threads=[]
    29     for i in range(5):
    30         threads.append(myThread())
    31     for t in threads:
    32         t.start()
    33     for t in threads:
    34         t.join()
    deadLock

    解决办法:使用递归锁

      即重新定义一把锁:lock = threading.RLock() --> 递归锁

      将所有的锁替换为递归锁即可。递归锁可以重复加锁。

      RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

    3.5 信号量(Semaphore)--> 相当于一把锁

      信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1。

      计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)

      BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。

     1 import threading,time
     2 class myThread(threading.Thread):
     3     def run(self):
     4         if semaphore.acquire():
     5             print(self.name)
     6             time.sleep(5)
     7             semaphore.release()
     8 if __name__=="__main__":
     9     semaphore=threading.Semaphore(5)
    10     thrs=[]
    11     for i in range(100):
    12         thrs.append(myThread())
    13     for t in thrs:
    14         t.start()
    Semaphore

    3.6 条件变量同步(Condition)--> 锁

      有一类线程需要满足条件之后才能够继续执行,Python提供了threading.Condition 对象用于条件变量线程的支持,它除了能提供RLock()或Lock()的方法外,还提供了 wait()、notify()、notifyAll()方法。

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

    1 wait():条件不满足时调用,线程会释放锁并进入等待阻塞;
    2 notify():条件创造后调用,通知等待池激活一个线程;
    3 notifyAll():条件创造后调用,通知等待池激活所有线程。
     1 import threading,time
     2 from random import randint
     3 class Producer(threading.Thread):
     4     def run(self):
     5         global L
     6         while True:
     7             val=randint(0,100)
     8             print('生产者',self.name,":Append"+str(val),L)
     9             if lock_con.acquire():
    10                 L.append(val)
    11                 lock_con.notify()
    12                 lock_con.release()
    13             time.sleep(3)
    14 class Consumer(threading.Thread):
    15     def run(self):
    16         global L
    17         while True:
    18                 lock_con.acquire()
    19                 if len(L)==0:
    20                     lock_con.wait()
    21                 print('消费者',self.name,":Delete"+str(L[0]),L)
    22                 del L[0]
    23                 lock_con.release()
    24                 time.sleep(0.25)
    25 
    26 if __name__=="__main__":
    27 
    28     L=[]
    29     lock_con=threading.Condition()
    30     threads=[]
    31     for i in range(5):
    32         threads.append(Producer())
    33     threads.append(Consumer())
    34     for t in threads:
    35         t.start()
    36     for t in threads:
    37         t.join()
    Condition Demo

    3.7 同步条件(Event)

    条件同步和条件变量同步差不多意思,只是少了锁功能,因为条件同步设计于不访问共享资源的条件环境。event=threading.Event():条件环境对象,初始值 为False;

    1 event.isSet():返回event的状态值;
    2 
    3 event.wait():如果 event.isSet()==False将阻塞线程;
    4 
    5 event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
    6 
    7 event.clear():恢复event的状态值为False。

    示例:

     1 import threading,time
     2 class Boss(threading.Thread):
     3     def run(self):
     4         print("BOSS:今晚大家都要加班到22:00。")
     5         event.isSet() or event.set()
     6         time.sleep(5)
     7         print("BOSS:<22:00>可以下班了。")
     8         event.isSet() or event.set()
     9 class Worker(threading.Thread):
    10     def run(self):
    11         event.wait()
    12         print("Worker:哎……命苦啊!")
    13         time.sleep(0.25)
    14         event.clear()
    15         event.wait()
    16         print("Worker:OhYeah!")
    17 if __name__=="__main__":
    18     event=threading.Event()
    19     threads=[]
    20     for i in range(5):
    21         threads.append(Worker())
    22     threads.append(Boss())
    23     for t in threads:
    24         t.start()
    25     for t in threads:
    26         t.join()
    View Code

    3.8 队列(queue)-->多线程利器

    queue中的方法:

    创建一个“队列”对象
    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
     2 from time import sleep
     3 from random import randint
     4 class Production(threading.Thread):
     5     def run(self):
     6         while True:
     7             r=randint(0,100)
     8             q.put(r)
     9             print("生产出来%s号包子"%r)
    10             sleep(1)
    11 class Proces(threading.Thread):
    12     def run(self):
    13         while True:
    14             re=q.get()
    15             print("吃掉%s号包子"%re)
    16 if __name__=="__main__":
    17     q=queue.Queue(10)
    18     threads=[Production(),Production(),Production(),Proces()]
    19     for t in threads:
    20         t.start()
    Demo
  • 相关阅读:
    青少年机器人技术等级考试实际操作试卷(三级)201812 new
    SQL预编译 new
    青少年机器人技术等级考试实际操作试卷(三级)201809 new
    青少年机器人技术等级考试实际操作试卷(三级)201803 new
    SQL基本练习 new
    Asp.Net MVC 自定义一个ActionResult用于AJAX交互
    使用 TeamLab 来协同和管理工作
    使用Chose来美化Select
    在Asp.Net下使用couchbase实现分布式缓存
    如何修改couchbase的RAM
  • 原文地址:https://www.cnblogs.com/horror/p/9343764.html
Copyright © 2020-2023  润新知