进程与线程之间的关系
线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。
进程就是一个程序在一个数据集上的一次动态执行过程。 进程一般由程序、数据集、进程控制块三部分组成。我们编写的程序用来描述进程要完成哪些功能以及如何完成;数据集则是程序在执行过程中所需要使用的资源;进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。
进程和线程的目的:提高执行效率
1、单进程单线程,主进程、主线程
2、自定义线程:
主进程
主线程
子线程
创建多进程,目的是为了利用CPU的多核,让CPU同时执行多个任务。
进程:
优点:同时利用多个CPU,能够同时进行多个操作
缺点:耗费资源(创建时重新开辟内存空间)
线程:
优点:共享同一进程内存,IO操作时,不依赖CPU,可并发操作
缺点:抢占资源
进程、线程越多越好吗?
进程不是越多越好,一般而言,CPU个数 = 进程个数
线程数,依赖任务而定,不是越多越好,每次记录线程的请求信息,上下文切换耗时。
IO密集型操作(不用CPU):多线程
计算密集型操作(使用CPU):多进程
python的进程上有个GIL 全局解释性锁,这个会造成,一个进程的多个线程,不能同时使用多个cpu,而是cpu每次只能选一个线程执行,因此,多线程在cpu执行的是无效的。但是在I/O操作的时候是可以同步的,比如time.sleep就是io 操作,多线程,可以同时等待
主线程
比如我们写的py文件,执行的时候,所有代码是如何向下执行呢?肯定有个主线程的。
再我们创建多线程时候,这些线程都是子线程,那肯定有个主线程的。
进程和程序关系 进程:程序实例,程序子集,有所谓生命周期,可以kill掉,比如你安装的word,是一个程序,你打开一个文档是一个进程,可以关掉。 进程要想完成并发执行的功能,就要进程切换。进程切换,上下文切换,运行进程,说明在cpu的寄存器里面有数据了。
一、Python的多线程或多进程概述
Python的多线程或多进程的调度是通过操作系统的调度程序实现的。当一个线程或进程阻塞,例如等待IO时,该线程或进程被操作系统降低执行的优先级,CPU内核可以被分配用来执行其它有实际计算任务的线程或进程。
Python的线程或进程架构:
线程存在于进程之内。一个进程可以包含多个线程,但通常包含至少一个线程,这个线程被称为主线程。在一个进程内的线程共享进程的内存,所以进程内的不同线程的通信可以通过引用共享的对象来实现。不同的进程并不共享同一块内存,所以进程间的通信是通过其它接口如文件、sockets或特别分配的共享内存区域来实现的。
当线程需要执行操作时,它请求操作系统的线程调度程序给其分配一些CPU时间。调度程序根据各种参数来将CPU的核心分配给等待的线程,调度程序的实现根据操作系统的不同而不同。同一个进程中运行的不同线程可能同时运行在不同的CPU核心上(但CPython例外)。
Python的线程和GIL
Python的CPython解释器包含一个全局解释器锁Global Interpreter Lock(GIL),它的存在确保了Python进程中同时只有一个线程可以执行,即使有多个CPU核心可用。所以CPython程序中的多线程并不能通过多个cpu核心并行执行。不过,即使是这样,在等待I/O时被阻塞的线程仍然被操作系统降低执行优先级并放入背景等待,以便让真正有计算任务的线程可以执行,下图简单地描述了这个过程:
上图中的Waiting for GIL状态是某个线程已经完成了I/O,在退出阻塞状态想要开始执行时,另外一个线程持有GIL,所以已经就绪的线程被强制等待。在很多的网络应用程序中,花在等待I/O上的时间比实际处理数据的时间要多得多。只要不是有非常大的并发连接数,由GIL导致的连接的线程的阻塞是相对较低的,所以对于这些网络服务程序,使用线程的方式实现并发连接,仍然是一个合适的架构。
Python的进程与线程的比较
对于操作系统来说,一个应用就是一个进程。比如打开一个浏览器,它是一个进程;打开一个记事本,它是一个进程。每个进程有它特定的进程号。他们共享操作系统的内存资源。进程是操作系统分配资源的最小单位。
而对于每一个进程而言,比如一个视频播放器,它必须同时播放视频和音频,就至少需要同时运行两个“子任务”,进程内的这些子任务就是通过线程来完成。线程是计算机执行任务的最小单元。一个进程它可以包含多个线程,这些线程相互独立,同时又共享进程所拥有的资源。
以使用OS资源的角度来说,进程比线程更加“重量级”,创建一个新的进程需要的时间比创建一个新线程来说要多,而且进程使用更多的内存资源。
有一点需要注意的是,如果你需要执行一个计算任务密集的Python程序,最好是通过多进程来实现。因为如果程序中的每个线程都有繁重的计算任务,它们都要使用CPU,而由于GIL的存在,这些线程并不能真正的在不同的CPU核心上并行执行,所以这会严重降低程序整体的性能。
Python多进程、多线程编程
Python中的多线程一般是通过其内置的threading模块提供的接口来实现的,多进程是通过multiprocessing模块实现的。本篇的代码实例是基于Python3的。
Python多进程编程
Process类
在Python中,通过multiprocessing模块内的Process类来创建新的进程。例如:

from multiprocessing import Process import os def info(title): print(title) print('module name:', __name__) if hasattr(os, 'getppid'): # only available on Unix print('parent process:', os.getppid()) print('process id:', os.getpid()) def f(name): info('function f') print('hello', name) if __name__ == '__main__': info('main line') p = Process(target=f, args=('bob',)) p.start() p.join() """ main line module name: __main__ parent process: 2128 process id: 3688 function f module name: __mp_main__ parent process: 3688 process id: 6884 hello bob """
通过Process类中的start方法来启动新的进程。有三种不同的启动方式:spawn,fork和forkserver。这三种方式的详细区别请参考官方文档。
Process类中的join方法的作用是等待子进程结束后再继续往下执行。
进程间通信
multiprocessing模块内包括两种进程间通信的方式:Queues和Pipes;同时也提供了其它方式在进程间共享状态,不过官方文档也提到在并发程序设计中尽量避免这种方式。
先来看一下Queues的例子:

from multiprocessing import Process, Queue def f(q): q.put([42, None, 'hello']) if __name__ == '__main__': q = Queue() p = Process(target=f, args=(q,)) p.start() print(q.get()) # prints "[42, None, 'hello']" p.join() """ [42, None, 'hello'] """
multiprocessing模块中的Queue几乎是queue.Queue的克隆,并且Queues是线程和进程安全的,这意味着多个线程或进程访问同一个Queue实例时,不会出现数据混乱的状况。下面来看一个使用Pipe的例子:

from multiprocessing import Process, Pipe def f(conn): conn.send([42, None, 'hello']) conn.close() if __name__ == '__main__': parent_conn, child_conn = Pipe() p = Process(target=f, args=(child_conn,)) p.start() print(parent_conn.recv()) # prints "[42, None, 'hello']" p.join() """ [42, None, 'hello'] """
Pipe方法返回一对连接对象,代表管道的两端,每个连接对象都有send()和recv()方法。如果不同的进程(或线程)在同一时刻试图在管道的同一端进行读写,数据可能会出现混乱,在不同端是没有问题的。Python中的对象大部分都不是进程(或线程)安全的,所以在不同的进程(或线程)操作同一对象时,需要使用同步机制。
进程的同步机制
进程的同步是通过Lock实现的。实例如下:

from multiprocessing import Process, Lock def f(l, i): l.acquire() try: print('hello world', i) finally: l.release() if __name__ == '__main__': lock = Lock() for num in range(10): Process(target=f, args=(lock, num)).start() """ hello world 1 hello world 3 hello world 0 hello world 2 hello world 7 hello world 6 hello world 4 hello world 8 hello world 9 hello world 5 """
使用进程池
Pool类提供了一个批量创建多个子进程的方法,可以给定子进程数的上限,避免无限地消耗系统的资源。看一下官方文档的实例:

from multiprocessing import Pool, TimeoutError import time import os def f(x): return x*x if __name__ == '__main__': # start 4 worker processes with Pool(processes=4) as pool: # print "[0, 1, 4,..., 81]" print(pool.map(f, range(10))) # print same numbers in arbitrary order for i in pool.imap_unordered(f, range(10)): print(i) # evaluate "f(20)" asynchronously res = pool.apply_async(f, (20,)) # runs in *only* one process print(res.get(timeout=1)) # prints "400" # evaluate "os.getpid()" asynchronously res = pool.apply_async(os.getpid, ()) # runs in *only* one process print(res.get(timeout=1)) # prints the PID of that process # launching multiple evaluations asynchronously *may* use more processes multiple_results = [pool.apply_async(os.getpid, ()) for i in range(4)] print([res.get(timeout=1) for res in multiple_results]) # make a single worker sleep for 10 secs res = pool.apply_async(time.sleep, (10,)) try: print(res.get(timeout=1)) except TimeoutError: print("We lacked patience and got a multiprocessing.TimeoutError") print("For the moment, the pool remains available for more work") # exiting the 'with'-block has stopped the pool print("Now the pool is closed and no longer available") """ [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 0 1 4 9 16 25 36 49 64 81 400 8652 [8652, 8652, 8652, 8652] We lacked patience and got a multiprocessing.TimeoutError For the moment, the pool remains available for more work Now the pool is closed and no longer available """
Python多线程编程
Python中使用线程的方式有很多都与使用进程的方式类似,这里就不再列出实例代码。
Thread类
在Python中,通过threading模块内的Thread类来创建新的进程。通过Thread类中的start方法启动线程,join方法等待线程结束。
线程间通信
线程间最简单的通信方式是通过threading模块内的Event类。当然也可以通过共享对象来在线程间共享状态信息。由于queue.Queue是线程和进程安全的,所以它也是线程通信使用理想对象。其它对象大部分都不是线程(或进程)安全的,所以在不同的线程(或进程)操作同一对象时,需要使用同步机制。
线程的同步机制
threading模块中提供了Lock,RLock,Condition,Semaphore等类进行线程间的同步。
关于threading模块中更详细的使用信息,请参考官方文档。
内容来源与参考:
https://andyyoung01.github.io/2017/01/21/Python%E7%9A%84%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B8%8E%E5%A4%9A%E8%BF%9B%E7%A8%8B%E7%AE%80%E4%BB%8B-%E4%B8%8A/#Python的线程和GIL
https://andyyoung01.github.io/2017/01/22/Python%E7%9A%84%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B8%8E%E5%A4%9A%E8%BF%9B%E7%A8%8B%E7%AE%80%E4%BB%8B-%E4%B8%8B/
Python内的线程进程协程模块实现
threading模块
threading 模块建立在 _thread 模块之上。thread 模块以低级、原始的方式来处理和控制线程,而 threading 模块通过对 thread 进行二次封装,提供了更方便的 api 来处理线程。
threading基于Java的线程模型设计。锁(Lock)和条件变量(Condition)在Java中是对象的基本行为(每一个对象都自带了锁和条件变量),而在Python中则是独立的对象,所以python的threading模块中还提供了Lock,Rlock,Condition,Event等常用类,它们在python中是独立于Tread模块的,但是却与线程紧密相关,不可分割。
需要注意的是:python的线程中没有优先级、线程组,也不能被停止、暂停、恢复、中断,线程只能随着线程中的代码执行完毕而被销毁。在实现线程池的时候无法停止已经注入了方法且执行超时的线程。
创建线程的两种方式
第一种创建线程的方式 创建5个线程

import threading import time def worker(num): time.sleep(3) print("Thread %d" % num) return for i in range(5): t = threading.Thread(target=worker, args=(i,)) t.start() """ Thread 0 Thread 1 Thread 2 Thread 4 Thread 3 """
第二种创建线程的方式 创建5个线程

import threading import time class MyThread(threading.Thread): def __init__(self, name): # threading.Thread.__init__(self) super(MyThread, self).__init__(target=self.fun, name="t %d" % i) self.name = name def fun(self): time.sleep(2) print("name %s thread %s" % (self.name, threading.current_thread().name)) for i in range(5): t = MyThread(i) t.start() """ name 0 thread 0 name 2 thread 2 name 1 thread 1 name 4 thread 4 name 3 thread 3 """
thread线程类的方法说明
t.start() : 激活线程,
t.getName() : 获取线程的名称
t.setName() : 设置线程的名称
t.name : 获取或设置线程的名称
t.is_alive() : 判断线程是否为激活状态
t.isAlive() :判断线程是否为激活状态
t.setDaemon() 设置为后台线程或前台线程(默认:False);通过一个布尔值设置线程是否为守护线程,必须在执行start()方法之后才可以使用。如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止;如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
t.isDaemon() : 判断是否为守护线程
t.ident :获取线程的标识符。线程标识符是一个非零整数,只有在调用了start()方法之后该属性才有效,否则它只返回None。
t.join() :逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
t.run() :线程被cpu调度后自动执行线程对象的run方法
join 代码

import time import threading def printNum(a): print('num:',a) time.sleep(1) def ThreadTest(i): return threading.Thread(target=printNum, args=(999,)) thread_arr = [] for i in range(5): t = ThreadTest(i) thread_arr.append(t) for t in thread_arr: t.start() for t in thread_arr: t.join() print('finished') """ num: 999 num: 999 num: 999 num: 999 num: 999 finished """
多线程的说明:
threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。python当前版本的多线程库没有实现优先级、线程组,线程也不能被停止、暂停、恢复、中断。
threading模块提供的类:
Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, local。
threading 模块提供的常用方法:
threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
threading 模块提供的常量:
threading.TIMEOUT_MAX 设置threading全局超时时间。
Thread是线程类,有两种使用方法,直接传入要运行的方法或从Thread继承并覆盖run():

import threading import time # 方法一:将要执行的方法作为参数传给Thread的构造方法 def action(arg): time.sleep(1) print('the arg is:%s\r' % arg) for i in range(4): t = threading.Thread(target=action, args=(i,)) t.start() print('main thread end!') """ main thread end! the arg is:0 the arg is:3 """ # 方法二:从Thread继承,并重写run() class MyThread(threading.Thread): def __init__(self, arg): super(MyThread, self).__init__() # 注意:一定要显式的调用父类的初始化函数。 self.arg = arg def run(self): # 定义每个线程要运行的函数 time.sleep(1) print('the arg is:%s\r' % self.arg) for i in range(4): t = MyThread(i) t.start() print('main thread end!') """ main thread end! the arg is:1 the arg is:2 the arg is:3 """
Lock、Rlock类
由于线程之间随机调度:某线程可能在执行n条后,CPU接着执行其他线程。为了多个线程同时操作一个内存中的资源时不产生混乱,我们使用锁。
Lock(指令锁)是可用的最低级的同步指令。Lock处于锁定状态时,不被特定的线程拥有。Lock包含两种状态——锁定和非锁定,以及两个基本的方法。
可以认为Lock有一个锁定池,当线程请求锁定时,将线程置于池中,直到获得锁定后出池。池中的线程处于状态图中的同步阻塞状态。
RLock(可重入锁)是一个可以被同一个线程请求多次的同步指令。RLock使用了“拥有的线程”和“递归等级”的概念,处于锁定状态时,RLock被某个线程拥有。拥有RLock的线程可以再次调用acquire(),释放锁时需要调用release()相同次数。
可以认为RLock包含一个锁定池和一个初始值为0的计数器,每次成功调用 acquire()/release(),计数器将+1/-1,为0时锁处于未锁定状态。
简言之:Lock属于全局,Rlock属于线程。
构造方法:
Lock(),Rlock(),推荐使用Rlock()
实例方法:
acquire([timeout]): 尝试获得锁定。使线程进入同步阻塞状态。
release(): 释放锁。使用前线程必须已获得锁定,否则将抛出异常。
线程锁threading.RLock和threading.Lock
我们使用线程对数据进行操作的时候,如果多个线程同时修改某个数据,可能会出现不可预料的结果,为了保证数据的准确性,引入了锁的概念。
例:假设列表A的所有元素就为0,当一个线程从前向后打印列表的所有元素,另外一个线程则从后向前修改列表的元素为1,那么输出的时候,列表的元素就会一部分为0,一部分为1,这就导致了数据的不一致。锁的出现解决了这个问题。
View Code
Lock与Rlock 对比

import threading lock = threading.Lock() #Lock对象 lock.acquire() lock.acquire() #产生了死锁。 lock.release() lock.release() import threading rLock = threading.RLock() #RLock对象 rLock.acquire() rLock.acquire() #在同一线程内,程序不会堵塞。 rLock.release() rLock.release() lock vs rlock Code
未使用锁
多次运行可能产生混乱。这种场景就是适合使用锁的场景。

import threading import time gl_num = 0 def show(arg): global gl_num time.sleep(0.00001) gl_num += 1 print(gl_num) for i in range(5): t = threading.Thread(target=show, args=(i,)) t.start() print('main thread stop') """ 1 2 3 main thread stop 4 5 """
使用锁
全局变量在每次被调用时都要获得锁,才能操作,因此保证了共享数据的安全性。

import threading import time gl_num = 0 lock = threading.RLock() # 调用acquire([timeout])时,线程将一直阻塞,直到获得锁定或者直到timeout秒后(timeout参数可选)。 # 返回是否获得锁。 def Func(): lock.acquire() global gl_num gl_num += 1 time.sleep(0.00001) print(gl_num) lock.release() for i in range(5): t = threading.Thread(target=Func) t.start() """ 1 2 3 4 5 """
Condition类
条件变量对象能让一个线程停下来,等待其它线程满足了某个“条件”。如,状态的改变或值的改变。
Condition(条件变量)通常与一个锁关联。需要在多个Contidion中共享一个锁时,可以传递一个Lock/RLock实例给构造方法,否则它将自己生成一个RLock实例。
可以认为,除了Lock带有的锁定池外,Condition还包含一个等待池,池中的线程处于等待阻塞状态,直到另一个线程调用notify()/notifyAll()通知;得到通知后线程进入锁定池等待锁定。
一个condition变量总是与某些类型的锁相联系,这个可以使用默认的情况或创建一个,当几个condition变量必须共享和同一个锁的时候,是很有用的。锁是conditon对象的一部分:没有必要分别跟踪。
condition变量服从上下文管理协议:with语句块封闭之前可以获取与锁的联系。 acquire() 和 release() 会调用与锁相关联的相应的方法。
其他和锁关联的方法必须被调用,wait()方法会释放锁,当另外一个线程使用 notify() or notify_all()唤醒它之前会一直阻塞。一旦被唤醒,wait()会重新获得锁并返回
Condition(lock=None)
构造方法:
Condition([lock/rlock])
实例方法:
- acquire([timeout])/release(): 调用关联的锁的相应方法,给线程上锁/解锁。
- wait([timeout]): 调用这个方法将使线程进入Condition的等待池等待通知,并释放锁。使用前线程必须已获得锁定,否则将抛出异常。
- notify(): 调用这个方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用acquire()尝试获得锁定(进入锁定池);其他线程仍然在等待池中。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。
- notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。
比较经典的例子是生产者与消费者模型,代码中写了两个类,Consumer和Producer,分别继承了Thread类,分别初始化这两个类获得了c和p对象,并启动这两个线程。则这两个线程去执行run方法(这里与Thread类内部的调度有关),定义了producer全局变量和condition对象为全局变量,当producer不大于1时,消费者线程被condition对象阻塞,不能继续消费(这里是不再递减),当producer不小于10时,生产者线程被condition对象阻塞,不再生产(这里是不再累加),代码在下面,拿去执行,断点一下就明白了。

import threading import time condition = threading.Condition() products = 0 class Producer(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): global condition, products while True: if condition.acquire(): if products < 10: products += 1; print("Producer(%s):deliver one, now products:%s" %(self.name, products)) condition.notify() else: print("Producer(%s):already 10, stop deliver, now products:%s" %(self.name, products)) condition.wait(); condition.release() time.sleep(2) class Consumer(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): global condition, products while True: if condition.acquire(): if products > 1: products -= 1 print("Consumer(%s):consume one, now products:%s" %(self.name, products)) condition.notify() else: print("Consumer(%s):only 1, stop consume, products:%s" %(self.name, products)) condition.wait(); condition.release() time.sleep(2) if __name__ == "__main__": for p in range(0, 2): p = Producer() p.start() for c in range(0, 10): c = Consumer() c.start() """ Producer(Thread-1):deliver one, now products:1 Producer(Thread-2):deliver one, now products:2 Consumer(Thread-3):consume one, now products:1 Consumer(Thread-4):only 1, stop consume, products:1 Consumer(Thread-5):only 1, stop consume, products:1 ..... """
Event类
通用的条件变量。多个线程可以等待某个事件的发生,在事件发生后,所有的线程都会被激活。
Event(事件)是最简单的线程通信机制之一:一个线程通知事件,其他线程等待事件。Event内置了一个初始为False的标志,当调用set()时设为True,调用clear()时重置为 False。wait()将阻塞线程至等待阻塞状态。
Event其实就是一个简化版的 Condition。Event没有锁,无法使线程进入同步阻塞状态。
构造方法:
Event()
实例方法:
- isSet(): 当内置标志为True时返回True。
- set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。
- clear(): 将标志设为False。
- wait([timeout]): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。
这是一个比较关键的类,它的意义在于可以控制属于同一个线程类的多个实例化对象,让他们同时阻塞或者执行。配合队列来实现一个线程池非常好用。
!!线程之间交互 threading.Event方法 红灯 绿灯 信号标志位!!

import threading import time import random def light(): if not event.isSet(): #没有设置的话 event.set() # 设置绿灯 count = 0 #计数器秒数 while True: if count < 10: #小于十秒 是绿灯 print("\033[42;1m ------green light on ----\033[0m") elif count < 13: #小于13秒 大于10秒 是黄灯 print("\033[43;1m ------yellow light on ----\033[0m") elif count < 20: #小于于20秒 有设置则取消 if event.isSet(): event.clear() print("\033[41;1m ------red light on ----\033[0m") else: #大于20 重新 count = 0 #取消秒数计时 event.set() #重新变为绿灯 time.sleep(1) count +=1 def car(n): # 第二个线程 车线程 while 1: time.sleep(random.randrange(3)) #随机等待三秒 if event.isSet(): print("car [%s] is running..." % n) #如果被设置了信号则是绿灯,该线程的车即可通过 else: #否则的话提示红灯 print("car [%s] is waitting for the red light.." %n) event.wait() #红灯的话,会在此处卡住,不往下执行 print("Green light is on ,car %s is running......." %n) if __name__ == '__main__': #下面是定义了两个线程 ,灯线程 车线程, threading.Event用来设置标着符号让两个线程交流 event = threading.Event() Light = threading.Thread(target=light) Light.start() for i in range(3): t = threading.Thread(target=car,args=(i,)) t.start() """ ------green light on ---- car [2] is running... car [0] is running... ------yellow light on ---- car [1] is running... car [1] is running... car [2] is running... car [0] is running... ------red light on ---- car [1] is waitting for the red light.. ...... """
queue模块
Queue 就是对队列,它是线程安全的
举例来说,我们去肯德基吃饭。厨房是给我们做饭的地方,前台负责把厨房做好的饭卖给顾客,顾客则去前台领取做好的饭。这里的前台就相当于我们的队列。
这个模型也叫生产者-消费者模型。

import queue q = queue.Queue(maxsize=0) # 构造一个先进先出队列,maxsize指定队列长度,为0 时,表示队列长度无限制。 q.join() # 等到队列为空的时候,再执行别的操作 q.qsize() # 返回队列的大小 (不可靠) q.empty() # 当队列为空的时候,返回True 否则返回False (不可靠) q.full() # 当队列满的时候,返回True,否则返回False (不可靠) q.put(item, block=True, timeout=None) # 将item放入Queue尾部,item必须存在,可以参数block默认为True,表示当队列满时,会等待队列给出可用位置。为False时为非阻塞,此时如果队列已满,会引发queue.Full 异常。 可选参数timeout,表示 会阻塞设置的时间,过后,如果队列无法给出放入item的位置,则引发 queue.Full 异常 q.get(block=True, timeout=None) # 移除并返回队列头部的一个值,可选参数block默认为True,表示获取值的时候,如果队列为空,则阻塞,为False时,不阻塞,若此时队列为空,则引发 queue.Empty异常。 可选参数timeout,表示会阻塞设置的时候,过后,如果队列为空,则引发Empty异常。 q.put_nowait(item) # 等效于 put(item,block=False) q.get_nowait() # 等效于 get(item,block=False)