• python——线程相关


    使用python的threading中的Thread

    下面是两种基本的实现线程的方式:

    第一种方式————

    #coding=utf-8
    
    """
    thread的第一种声明及调用方式
    """
    
    from threading import Thread
    import time
    
    def func():
        while True:
            print time.ctime()
            time.sleep(1)
    
    def func1():
        while True:
            print "func1", time.ctime()
            time.sleep(2)
    
    #将要执行的方法作为参数传给Thread的构造方法
        
    t = Thread(target=func)
    t.start()
    
    t1 = Thread(target=func1)
    t1.start()

    第二种方式————

    #coding=utf-8
    
    """
    thread的第二种声明及调用方法
    """
    
    import time
    from threading import Thread
    from time import sleep
    
    #从Thread继承,并重写run()
    class myThread(Thread):
        def __init__(self, threadname):
            Thread.__init__(self, name=threadname)
        
        def run(self):
            while True:
                print self.getName() + " " + time.ctime()
                time.sleep(1)
                
    t = myThread("thread_1")
    t.start()
    
    t1 = myThread("thread_2")
    t1.start()
                

    接下来是:join和setDaemon的方法调用及具体区别:

    join的方法调用:

    #coding=utf-8
    
    """
    join方法的含义是:存在A主线程,B子线程,在A主线程中生成了一个子线程B,
    如果子线程B调用了join方法,则主线程需要等待子线程操作结束,才能够继续往下执行,主线程会被阻塞
    """
    import time
    from threading import Thread
    
    class myThread(Thread):
        def __init__(self, threadname):
            Thread.__init__(self, name = threadname)
        
        def run(self):
            time.sleep(10)
            print self.getName() + " " + time.ctime()
            
        
    def mainfunc():
        for i in range(5):
            print i    
        
    if __name__ == "__main__":
        t = myThread("childthread")
        t.start()
        #增加join方法的调用,则程序运行时的表现是:先子线程(会先sleep 10s,之后打印子线程的内容),再运行主线程中的打印从0到4的逻辑
        #不增加join方法的调用,则程序运行时的表现是:会先走主线程的打印从0到4的逻辑,之后子线程(sleep 10s,再打印子线程中的内容),但其实这里就是没有等待子线程
    #     t.join()
        mainfunc()

    setDaemon方法的调用:

    #coding=utf-8
    
    """
    daemon方法的含义是:存在主线程A,和子线程B,子线程B是从主线程A中生成的
    如果设置子线程B为daemon即守护进程,则主线程A不存在的话,子线程B就退出
    如果没有设置子线程B为daemon,则主线程A结束运行退出之后,子线程B也不退出
    """
    
    import time
    from threading import Thread
    
    class myThread(Thread):
        def __init__(self, threadname):
            Thread.__init__(self, name=threadname)
        
        def run(self):
            time.sleep(10)
            print self.getName() + " " + time.ctime()
        
    def mainfunc():
        for i in range(5):
            print i
    
    if __name__ == "__main__":
        t = myThread("zixiancheng")
        #不设置子线程为守护进程,则主线程退出,子线程继续运行
        #设置子线程为守护进程,则主线程退出,子线程直接退出
        #并且,需要在start之前调用setDaemon
    #     t.setDaemon(True)
        t.start()
        mainfunc()
        

     接下来是线程同步相关的内容:

    首先是没有线程同步机制的情况下,多个线程同时操作一个公共数据:

    代码如下:

    #coding=utf-8
    
    """
    @function: test Thread的Lock
    @author: LiXia
    @date: 2017-0303
    """
    
    """
    所有的锁都是为了在线程之间进行通信
    例如对一个公共变量进行操作,此例子为没有使用线程同步机制对公共变量进行操作
    """
    
    from threading import Thread
    import time
    
    data = 0
    
    class myThread(Thread):
        def __init__(self):
            Thread.__init__(self)
        
        def run(self):
            global data
            time.sleep(1)
            data += 1
            print self.getName() + " data: " + str(data)
                
    
    if __name__ == "__main__":
        for i in range(5):
            t = myThread()
            t.start()

    运行结果见下方:

    为了能够对公共操作的数据,在特定逻辑内保持一致,需要保证在该逻辑内,只有单个线程可以操作,其他线程需要等待该线程处理结束才可以。

    所以下面是关于python线程同步机制提供的指令:

    1、Lock: 

    但是将以上代码中增加上lock处理,注意一定要增加在对公共数据的逻辑判断和处理的前面和后面,所以这里必须添加到while判断之前,释放锁需要在输出操作结束之后添加

    即:

    #coding=utf-8
    
    """
    所有的锁都是为了在线程之间进行通信
    例如对一个公共变量进行操作
    """
    
    from threading import Thread
    import threading
    import time
    
    data = 0
    lock = threading.Lock()
    
    class myThread(Thread):
        def __init__(self):
            Thread.__init__(self)
        
        def run(self):
            global data
            if lock.acquire():
                time.sleep(1)
                data += 1
                print self.getName() + " data: " + str(data)
            lock.release()
                
    
    if __name__ == "__main__":
        for i in range(5):
            t = myThread()
            t.start()

    运行结果见下方:

     2、RLock

    代码如下:

    #coding=utf-8
    
    """
    通过RLock(可重入锁)进行线程间的安全通信,RLock为可重入锁,即线程在拥有RLock之后可以继续acquire(),只要保证acquire()次数与
    release()次数一致即可;
    继续对公共变量进行操作
    """
    
    from threading import Thread
    import threading
    import time
    
    rlock = threading.RLock()
    data = 0
    
    class myThread(Thread):
        def __init__(self):
            Thread.__init__(self)
        
        def run(self):
            global data
            if rlock.acquire():
                time.sleep(1)
                data += 1
                print self.getName() + " data:" + str(data)
                
                if rlock.acquire():
                    time.sleep(1)
                    data -= 1
                    print self.getName() + " data:" + str(data)
                    rlock.release() 
                rlock.release()
                
        
    if __name__ == "__main__":
        for i in range(5):
            t = myThread()
            t.start()

    运行结果:

    3、Condition

    Condition就是条件变量,condition通常与一个锁关联。

    acquire([timeout])/release(): 调用关联的锁的相应方法。 
    wait([timeout]): 调用这个方法将使线程进入Condition的等待池等待通知,并释放锁。使用前线程必须已获得锁定,否则将抛出异常。 
    notify(): 调用这个方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用acquire()尝试获得锁定(进入锁定池);其他线程仍然在等待池中。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。 
    notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。

    #coding=utf-8
    
    """
    以最经典的生产者和消费者的问题为例,两者需要针对一个公共的数据区域做操作,并且两端需要进行通信
    生产者在生产之前,需要先占领地盘,然后开始生产,生产结束通知消费者来取;
    消费者在消费的时候,也需要先占领地盘,然后开始消费,消费结束通知生产者继续生产;
    当然还有多个生产者和消费者的情况,会更加复杂,可以用python的Queue来实现
    """
    
    from threading import Thread
    import threading
    import time
    import random
    
    product = []
    con = threading.Condition()    #得到一个Condition的对象
    
    #生产者线程
    class producerThread(Thread):
        def __init__(self, threadname):
            Thread.__init__(self, name=threadname)
        
        def run(self):
            global product
            if con.acquire():
                while True:
                    r = random.randint(0, 20)
                    product.append(r)
                    print "product len and product: ", self.getName(), product
                    con.notify()
                    con.wait()
    #                 time.sleep(2)
    
    #消费者线程    
    class consumerThread(Thread):
        def __init__(self, threadname):
            Thread.__init__(self, name=threadname)
        
        def run(self):
            global product
            if con.acquire():
                while True:
                    if product != []:
                        product.pop()
                        print "consumer len and product: ", self.getName(), product
                        con.notify()
                    con.wait()
                    time.sleep(2)
            
    if __name__ == "__main__":
        t1 = producerThread("producer")
        t2 = consumerThread("consumer")
        
        t1.start()
        t2.start()
        

    运行结果如下:

     4、对多个生产者消费者支持的很好的Queue

    Queue是什么?Queue实现了多生产者-多消费者的队列形式,分别包含先入先出(默认)FIFO、后进先出LIFO、以及优先级队列PriorityQueue(优先级低的先出)

    Queue模块的class及exception:

    #coding=utf-8
    
    """
    Queue的类和异常
    """
    
    import Queue
    
    q = Queue.Queue(maxsize = 0)    #FIFO的Queue,maxsize即Queue的size值,maxsize≤0表示无size限制
    
    lq = Queue.LifoQueue(maxsize = 0) #LIFO的Queue,先进后出,maxsiz的含义同上
    
    pq = Queue.PriorityQueue(maxsize = 0) #优先级Queue,优先级低的先出,maxsize的含义同上
    
    Queue.Empty()  #当Queue的对象为空时,使用非阻塞get时会抛出异常
    
    Queue.Full() #当Queue的对象已满时,使用非阻塞put时会抛出异常

    Queue的对象:

    Queue(Queue、LifoQueue、PriorityQueue)的对象提供的公共方法如下:

    #coding=utf-8
    
    """
    Queue(Queue、LifoQueue、PriorityQueue)的对象的公共方法
    """
    
    import Queue
    
    q = Queue.Queue()
    
    q.qsize()   #获取Queue的大小
    q.empty()   #是否为空,是则返回True,否则返回False
    q.full()   #是否已满,是则返回True,否则返回False
    q.put(item[, block[, timeout]])    #put元素到Queue中,可选参数block默认值为True,timeout默认值为None,即为阻塞方法,
    #接上,若timeout为正值,则代表会阻塞timeout的时长之后引发一个full的异常(如果已满)
    q.put_nowait(item) #相当于q.put(item, False)
    q.get([block[, timeout]])  #get元素到Queue中,可选参数block默认值为True,timeout默认值为None,即为阻塞方法,
    #接上,若timeout为正数,则代表会则色timeouu的时长之后会引发一个empty的异常(如果为空)
    q.get_nowait() #相当于q.get(False)
    q.task_done() #用在Queue的消费者模式下,即用在Queue.get()方法之后,通过Queue.task_done()告诉Queue当前任务完成
    #如果当前join正在阻塞,则它将在所有item都被处理之后恢复
    q.join() #会保持阻塞直到队列中的所有item都会处理
    #接上,未完成的task数目会增加当一个item被add到Queue中,未完成的task数目会降低,每当消费者线程调用task_done来表示所有的
    #接上,work都完成了,当未完成的数目降低到0时,join就自动取消阻塞了

    Queue的主要用法是什么?

    看一下Queue的源码实现,然后在另一篇博客中更新一下。

    #coding=utf-8
    
    """
    理解Queue的用法,理解Queue衍生出来的不同的class
    """
    
    from Queue import Queue
    from threading import Thread
    import time
    import random
    
    q = Queue()
    
    class producerQueue(Thread):
        def __init__(self, queue):
            Thread.__init__(self)
            self.queue = queue
        
        def run(self):
            while True:
                r = random.randint(0, 99)
                self.queue.put(r)
                print self.getName() + "produce " + str(r)
                time.sleep(1)
        
    class consumerQueue(Thread):
        def __init__(self, queue):
            Thread.__init__(self)
            self.queue = queue
        
        def run(self):
            while True:
                r = self.queue.get()
                print self.getName() + "consume " + str(r)
                time.sleep(3)
                self.queue.task_done()
        
    if __name__ == "__main__":
        threads = []
        for i in range(5):
            t = producerQueue(q)
            t.start()
    #         threads.append(t)
         
        for i in range(3):
            t = consumerQueue(q)
            t.start()
    #         threads.append(t)
         
        q.join()

    之后得到的结果如下:

  • 相关阅读:
    难以实践敏捷:估算
    使用AsyncEnumerator简化异步操作
    ESXi 入门配置
    学习模式,不如先了解问题
    我应该用哪种虚拟机?(一)
    在2003上实现Custom Task Pane
    我应该用哪种虚拟机?(终)
    我应该用哪种虚拟机?(二)
    正则表达式周二挑战赛 第十二周
    [译]Node中的ES6特性
  • 原文地址:https://www.cnblogs.com/keke-xiaoxiami/p/6492423.html
Copyright © 2020-2023  润新知