进程线程

 

进程与线程之间的关系

线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。

 

进程就是一个程序在一个数据集上的一次动态执行过程。 进程一般由程序、数据集、进程控制块三部分组成。我们编写的程序用来描述进程要完成哪些功能以及如何完成;数据集则是程序在执行过程中所需要使用的资源;进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。

 

 

进程和线程的目的:提高执行效率


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的线程或进程架构:
“Multi-threading”

 

线程存在于进程之内。一个进程可以包含多个线程,但通常包含至少一个线程,这个线程被称为主线程。在一个进程内的线程共享进程的内存,所以进程内的不同线程的通信可以通过引用共享的对象来实现。不同的进程并不共享同一块内存,所以进程间的通信是通过其它接口如文件、sockets或特别分配的共享内存区域来实现的。


当线程需要执行操作时,它请求操作系统的线程调度程序给其分配一些CPU时间。调度程序根据各种参数来将CPU的核心分配给等待的线程,调度程序的实现根据操作系统的不同而不同。同一个进程中运行的不同线程可能同时运行在不同的CPU核心上(但CPython例外)。

 

Python的线程和GIL

Python的CPython解释器包含一个全局解释器锁Global Interpreter Lock(GIL),它的存在确保了Python进程中同时只有一个线程可以执行,即使有多个CPU核心可用。所以CPython程序中的多线程并不能通过多个cpu核心并行执行。不过,即使是这样,在等待I/O时被阻塞的线程仍然被操作系统降低执行优先级并放入背景等待,以便让真正有计算任务的线程可以执行,下图简单地描述了这个过程:


“Threading-GIL”

 

上图中的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
"""
View Code

 

通过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']
"""
View Code

 

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']
"""
View Code

 

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
"""
View Code

 

使用进程池

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
    """
View Code

 

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
"""
View Code

 

第二种创建线程的方式 创建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
"""
View Code

 

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
"""
View Code

 

 

多线程的说明

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
"""
View Code

 

 

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
View 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
"""
View Code

 

使用锁

全局变量在每次被调用时都要获得锁,才能操作,因此保证了共享数据的安全性。

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
"""
View Code

 

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
.....
"""
View Code

 

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..
......
"""
View Code

 

 

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)
View Code

 

posted on 2017-04-20 10:16  trp  阅读(424)  评论(0编辑  收藏  举报

导航