python实现线程安全队列

     最近学习Spark,我主要使用pyspark api进行编程

   之前使用Python都是现学现用,用完就忘了也没有理解和记忆,因此这里把Python相关的知识也弥补和记录下来吧

   多线程任务队列在实际项目中非常有用,关键的地方要实现队列的多线程同步问题,也即保证队列的多线程安全

   例如:可以开多个消费者线程,每个线程上绑定一个队列,这样就实现了多个消费者同时处理不同队列上的任务

              同时可以有多个生产者往队列发送消息,实现异步消息处理

   先复习下互斥量条件变量的概念:

   互斥量(mutex)从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥锁加锁的线程将会阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为运行状态的线程可以对互斥锁加锁,其他线程将会看到互斥锁依然被锁住,只能回去再次等待它重新变为可用。

   条件变量(cond)是在多线程程序中用来实现"等待--》唤醒"逻辑常用的方法。条件变量利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使“条件成立”。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。线程在改变条件状态前必须首先锁住互斥量,函数pthread_cond_wait把自己放到等待条件的线程列表上,然后对互斥锁解锁(这两个操作是原子操作)。在函数返回时,互斥量再次被锁住

    条件变量总是与互斥锁一起使用的

    python的threading中定义了两种锁:threading.Lock和threading.RLock

   两者的不同在于后者是可重入锁,也就是说在一个线程内重复LOCK同一个锁不会发生死锁,这与POSIX中的PTHREAD_MUTEX_RECURSIVE也就是可递归锁的概念是相同的, 互斥锁的API有三个函数,分别执行分配锁,上锁,解锁操作。

threading.Lock()        分配一个互斥锁
acquire([blocking=1])   上锁(阻塞或者非阻塞,非阻塞时相当于try_lock,通过返回False表示已经被其它线程锁住。)
release()               解锁
     python的threading中的条件变量默认绑定了一个RLock,也可以在初始化条件变量的时候传进去一个自己定义的锁.

    

threading.Condition([lock])  分配一个条件变量
acquire(*args)               条件变量上锁
release()                    条件变量解锁
wait([timeout])              等待唤醒,timeout表示超时
notify(n=1)                  唤醒最大n个等待的线程
notifyAll()、notify_all()    唤醒所有等待的线程

   最后贴出我自己实现的简单线程安全任务队列

#encoding='utf-8'
#ConcurrentQueue.py
import Queue
import threading

class ConcurrentQueue:

    def __init__(self, capacity = -1):
        self.__capacity = capacity          #初始化队列大小
        self.__mutex = threading.Lock()     #初始化互斥量
        self.__cond  = threading.Condition(self.__mutex)    #初始化条件变量
        self.__queue = Queue.Queue()        #初始化队列

    def get(self):

        if  self.__cond.acquire():          #获取互斥锁和条件变量,python中threading条件变量默认包含互斥量,因此只需要获取条件变量即可
            while self.__queue.empty():
                self.__cond.wait()          #条件变量等待
             
            elem = self.__queue.get()

            self.__cond.notify()
            self.__cond.release()

        return elem

    def put(self,elem):

        if self.__cond.acquire():
            while self.__queue.qsize() >= self.__capacity:
                self.__cond.wait()
            self.__queue.put(elem)

            self.__cond.notify()
            self.__cond.release()

    def clear(self):

        if self.__cond.acquire():

            self.__queue.queue.clear()

            self.__cond.release()
            self.__cond.notifyAll()

    def empty(self):

        is_empty = False;
        if self.__mutex.acquire():            #只需要获取互斥量
            is_empty = slef.__queue.empty()
            self.__mutex.release()

        return is_empty

    def size(self):
        size = 0
        if self.__mutex.acquire():
            size = self.__queue.qsize()
            self.__mutex.release()

        return size

    def resize(self,capacity = -1):
        self.__capacity = capacity

 测试代码

class CQTest2():
    
    def __init__(self):
        self.queue = ConcurrentQueue(10)

    def consumer(self):
        while True:
            task = self.queue.get()

            print 'get ', task, ' from queue'    #print是非线程安全的,因此如果又多个消费者会打印混乱,可以使用logging输出日志到文件查看
            

    def producer(self):
        while True:
            for i in range(10):
                self.queue.put(i)
                #print 'put ', i , ' into queue'    #

    def run(self):
        t1 = threading.Thread(target = self.consumer)
        t2 = threading.Thread(target = self.producer)

        t1.start()
        t2.start()

        #t1.join()
        #t2.join()

if __name__ == '__main__':
    cq_test = CQTest()
    cq_test.init_log()
    cq_test.run()

        

你可能感兴趣的:(python)