• python基础篇21-线程进程


    线程和进程

    什么是线程

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

    存在以下两种情况线程会停止运行,等待下一次CPU调度执行:

    1.CPU调度时间已到;

    2.当遇到IO阻塞(如sleep等)

    当调度时间已到,这时候CPU就会保存此时线程的执行状态,CPU就会进行上下文切换(即将CPU给其它线程执行)。当CPU再次向该线程调度时,就会读取该状态继续执行该线程

     

    什么是进程

    执行的程序就称为一个进程,进程是一堆计算资源(可以有线程,变量等)的集合。

    执行一个.py文件就是执行一个进程,这个进程中只有一个线程那就是主线程(如果手动创建线程,进程中就不只只有一个线程);在主线程中创建线程,这些线程和主线程并行运行

     

    进程与线程的区别

    1.一个进程中可以存在多个线程,而且多个线程之间可以共享进程的数据;每个进程有自己独立的地址空间;

    2.线程之间可以进行资源共享,进程之间不能进行资源共享;

    3.线程之间可以互相通信,进程之间不能互相通信;

    4.线程可以很容易创建,进程不容易创建,因为创建进程的开销大;

    5.线程之间可以互相操作,进程之间不行;

    6.主线程可以创建多个线程,主线程可以影响线程;主进程可以创建多个子进程,但是主进程不能影响子进程。

     

    python GIL(Global Interpreter Lock)   全局解释器锁

    在cpython解释器中才有GIL,它的作用是:在同一时刻,只能有一个线程进入解释器解释执行,这就导致了python在同一时刻只能使用一颗CPU,不能将线程分给多个CPU来执行即利用不了多核,实现不了多线程;但是可以实现多进程,可以通过多进程将进程分配到不同的CPU上执行(即同一时刻,同一个进程中只能允许一个线程进入解释器解释执行,但是允许不同进程中的线程同时进入解释器解释执行)。

     总结:

    在python里,如果处理的任务是IO密集型的任务,可以用多线程;如果处理的任务是计算密集型的,建议将多线程部分通过c语言来实现。

     

    threading模块

    线程的两种调用方式

    直接调用:

    import threading
    import time
     
    def sayhi(num): #定义每个线程要运行的函数
     
        print("running on number:%s" %num)
     
        time.sleep(3)
     
    if __name__ == '__main__':
     
        t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例
        t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例
     
        t1.start() #启动线程
        t2.start() #启动另一个线程
     
        print(t1.getName()) #获取线程名
        print(t2.getName())
    View Code

     继承式调用:

    import threading
    import time
     
     
    class MyThread(threading.Thread):
        def __init__(self,num):
            threading.Thread.__init__(self)
            self.num = num
     
        def run(self):#定义每个线程要运行的函数,这个方法是重写父类的run方法
     
            print("running on number:%s" %self.num)
     
            time.sleep(3)
     
    if __name__ == '__main__':
     
        t1 = MyThread(1)
        t2 = MyThread(2)
        t1.start()
        t2.start()
    View Code

    线程简单例子

    顺序执行:

    from time import ctime,sleep
    
    def music(func):
        for i in range(2):
            print("I was listening to %s. %s" %(func,ctime()))
            sleep(1)
    
    def move(func):
        for i in range(2):
            print("I was at the %s! %s" % (func, ctime()))
            sleep(5)
    
    if __name__ == '__main__':
        music(u'七里香')
        move(u'世界末路')
    #############执行完大概用时12s:顺序执行
    # I was listening to 七里香. Tue Mar 13 15:49:44 2018
    # I was listening to 七里香. Tue Mar 13 15:49:45 2018
    # I was at the 世界末路! Tue Mar 13 15:49:46 2018
    # I was at the 世界末路! Tue Mar 13 15:49:51 2018
    View Code

    线程执行:

    import threading
    from time import ctime,sleep
    import time
    
    def music(func):
        for i in range(2):
            print ("Begin listening to %s. %s" %(func,ctime()))
            sleep(1)  #遇到IO阻塞,线程就会解除对CPU的占用
            print("end listening %s"%ctime())
    
    def move(func):
        for i in range(2):
            print ("Begin watching at the %s! %s" %(func,ctime()))
            sleep(5)
            print('end watching %s'%ctime())
    
    threads = []
    t1 = threading.Thread(target=music,args=('七里香',))
    threads.append(t1)
    t2 = threading.Thread(target=move,args=('阿甘正传',))
    threads.append(t2)
    
    if __name__ == '__main__':
    
        for t in threads:
            t.start()
       
    #############执行完大概用时10s:通过线程执行
    # Begin listening to 七里香. Tue Mar 13 15:54:49 2018
    # Begin watching at the 阿甘正传! Tue Mar 13 15:54:49 2018
    # all over Tue Mar 13 15:54:49 2018
    # end listening Tue Mar 13 15:54:50 2018
    # Begin listening to 七里香. Tue Mar 13 15:54:50 2018
    # end listening Tue Mar 13 15:54:51 2018
    # end watching Tue Mar 13 15:54:54 2018
    # Begin watching at the 阿甘正传! Tue Mar 13 15:54:54 2018
    # end watching Tue Mar 13 15:54:59 2018
    View Code

    join & Daemon

    join():

    在子线程完成运行之前,这个子线程的父线程将一直被阻塞(即子线程执行完成后,再往下执行该子线程的父线程的后续代码)。

    import threading
    from time import ctime,sleep
    import time
    
    def music(func):
        for i in range(2):
            print ("Begin listening to %s. %s" %(func,ctime()))
            sleep(1)  #遇到IO阻塞,线程就会解除对CPU的占用
            print("end listening %s"%ctime())
    
    def move(func):
        for i in range(2):
            print ("Begin watching at the %s! %s" %(func,ctime()))
            sleep(5)
            print('end watching %s'%ctime())
    
    threads = []
    t1 = threading.Thread(target=music,args=('七里香',))
    threads.append(t1)
    t2 = threading.Thread(target=move,args=('阿甘正传',))
    threads.append(t2)
    
    if __name__ == '__main__':
    
        for t in threads:
            t.start()
        print ("all over %s" %ctime())
    
    #############执行完大概用时10s:通过线程执行
    # Begin listening to 七里香. Tue Mar 13 15:54:49 2018
    # Begin watching at the 阿甘正传! Tue Mar 13 15:54:49 2018
    # all over Tue Mar 13 15:54:49 2018
    # end listening Tue Mar 13 15:54:50 2018
    # Begin listening to 七里香. Tue Mar 13 15:54:50 2018
    # end listening Tue Mar 13 15:54:51 2018
    # end watching Tue Mar 13 15:54:54 2018
    # Begin watching at the 阿甘正传! Tue Mar 13 15:54:54 2018
    # end watching Tue Mar 13 15:54:59 2018
    情况一
    import threading
    from time import ctime,sleep
    import time
    
    def music(func):
        for i in range(2):
            print ("Begin listening to %s. %s" %(func,ctime()))
            sleep(1)  #遇到IO阻塞,线程就会解除对CPU的占用
            print("end listening %s"%ctime())
    
    def move(func):
        for i in range(2):
            print ("Begin watching at the %s! %s" %(func,ctime()))
            sleep(5)
            print('end watching %s'%ctime())
    
    threads = []
    t1 = threading.Thread(target=music,args=('七里香',))
    threads.append(t1)
    t2 = threading.Thread(target=move,args=('阿甘正传',))
    threads.append(t2)
    
    if __name__ == '__main__':
    
        for t in threads:
            t.start()
            t.join()  #等t1执行完成后再执行后续的代码
        print ("all over %s" %ctime())
    
    #用时12s
    # Begin listening to 七里香. Tue Mar 13 16:17:13 2018
    # end listening Tue Mar 13 16:17:14 2018
    # Begin listening to 七里香. Tue Mar 13 16:17:14 2018
    # end listening Tue Mar 13 16:17:15 2018
    # Begin watching at the 阿甘正传! Tue Mar 13 16:17:15 2018
    # end watching Tue Mar 13 16:17:20 2018
    # Begin watching at the 阿甘正传! Tue Mar 13 16:17:20 2018
    # end watching Tue Mar 13 16:17:25 2018
    # all over Tue Mar 13 16:17:25 2018
    情况二:程序成了串行执行
    import threading
    from time import ctime,sleep
    import time
    
    def music(func):
        for i in range(2):
            print ("Begin listening to %s. %s" %(func,ctime()))
            sleep(1)  #遇到IO阻塞,线程就会解除对CPU的占用
            print("end listening %s"%ctime())
    
    def move(func):
        for i in range(2):
            print ("Begin watching at the %s! %s" %(func,ctime()))
            sleep(5)
            print('end watching %s'%ctime())
    
    threads = []
    t1 = threading.Thread(target=music,args=('七里香',))
    threads.append(t1)
    t2 = threading.Thread(target=move,args=('阿甘正传',))
    threads.append(t2)
    
    if __name__ == '__main__':
    
        for t in threads:
            t.start()
        t2.join()
        print ("all over %s" %ctime())
    
    #用时10s
    # Begin listening to 七里香. Tue Mar 13 16:32:35 2018
    # Begin watching at the 阿甘正传! Tue Mar 13 16:32:35 2018
    # end listening Tue Mar 13 16:32:36 2018
    # Begin listening to 七里香. Tue Mar 13 16:32:36 2018
    # end listening Tue Mar 13 16:32:37 2018
    # end watching Tue Mar 13 16:32:40 2018
    # Begin watching at the 阿甘正传! Tue Mar 13 16:32:40 2018
    # end watching Tue Mar 13 16:32:45 2018
    # all over Tue Mar 13 16:32:45 2018
    情况三
    import threading
    from time import ctime,sleep
    import time
    
    def music(func):
        for i in range(2):
            print ("Begin listening to %s. %s" %(func,ctime()))
            sleep(1)  #遇到IO阻塞,线程就会解除对CPU的占用
            print("end listening %s"%ctime())
    
    def move(func):
        for i in range(2):
            print ("Begin watching at the %s! %s" %(func,ctime()))
            sleep(5)
            print('end watching %s'%ctime())
    
    threads = []
    t1 = threading.Thread(target=music,args=('七里香',))
    threads.append(t1)
    t2 = threading.Thread(target=move,args=('阿甘正传',))
    threads.append(t2)
    
    if __name__ == '__main__':
    
        for t in threads:
            t.start()
        t1.join()
        print ("all over %s" %ctime())
    
    #用时10s
    # Begin listening to 七里香. Tue Mar 13 16:39:59 2018
    # Begin watching at the 阿甘正传! Tue Mar 13 16:39:59 2018
    # end listening Tue Mar 13 16:40:00 2018
    # Begin listening to 七里香. Tue Mar 13 16:40:00 2018
    # end listening Tue Mar 13 16:40:01 2018
    # all over Tue Mar 13 16:40:01 2018
    # end watching Tue Mar 13 16:40:04 2018
    # Begin watching at the 阿甘正传! Tue Mar 13 16:40:04 2018
    # end watching Tue Mar 13 16:40:09 2018
    情况四

    守护进程(Daemon):

     将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。这个方法基本和join是相反的。当我们 在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如 果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是 只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 用setDaemon方法啦 。

    import threading
    from time import ctime,sleep
    import time
    
    def music(func):
        for i in range(2):
            print ("Begin listening to %s. %s" %(func,ctime()))
            sleep(1)  #遇到IO阻塞,线程就会解除对CPU的占用
            print("end listening %s"%ctime())
    
    def move(func):
        for i in range(2):
            print ("Begin watching at the %s! %s" %(func,ctime()))
            sleep(5)
            print('end watching %s'%ctime())
    
    threads = []
    t1 = threading.Thread(target=music,args=('七里香',))
    threads.append(t1)
    t2 = threading.Thread(target=move,args=('阿甘正传',))
    threads.append(t2)
    
    if __name__ == '__main__':
    
        for t in threads:
            t.setDaemon(True)
            t.start()
    
        print ("all over %s" %ctime())
        
    # Begin listening to 七里香. Tue Mar 13 17:14:16 2018
    # Begin watching at the 阿甘正传! Tue Mar 13 17:14:16 2018
    # all over Tue Mar 13 17:14:16 2018
    View Code

    其它方法:

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

     同步锁(Lock)

    同步锁引入:

    import time
    import threading
    
    def addNum():
        global num #在每个线程中都获取这个全局变量
        num-=1
    
    num = 100  #设定一个共享变量
    thread_list = []
    for i in range(100):         #开100个线程去减1
        t = threading.Thread(target=addNum)
        t.start()
        thread_list.append(t)
    
    for t in thread_list: #等待所有线程执行完毕
        t.join()
    
    print('final num:', num )
    
    #final num:0
    同步锁一
    import time
    import threading
    
    def addNum():
        global num #在每个线程中都获取这个全局变量
        temp = num
        num=temp-1
    
    num = 100  #设定一个共享变量
    thread_list = []
    for i in range(100):         #开100个线程去减1
        t = threading.Thread(target=addNum)
        t.start()
        thread_list.append(t)
    
    for t in thread_list: #等待所有线程执行完毕
        t.join()
    
    print('final num:', num )
    
    #final num:0
    同步锁二
    import time
    import threading
    
    def addNum():
        global num #在每个线程中都获取这个全局变量
        temp = num
        time.sleep(0.0001)
        num=temp-1
    
    num = 100  #设定一个共享变量
    thread_list = []
    for i in range(100):         #开100个线程去减1
        t = threading.Thread(target=addNum)
        t.start()
        thread_list.append(t)
    
    for t in thread_list: #等待所有线程执行完毕
        t.join()
    
    print('final num:', num )
    
    #final num: 92
    #原因:
    # 1:  why num-=1没问题呢?这是因为动作太快(完成这个动作在切换的时间内)
    # 2: if sleep(1),现象会更明显,100个线程每一个一定都没有执行完就进行了切换,我们说过sleep就等效于IO阻塞,1s之内不会再切换回来,所以最后的结果一定是99.
    同步锁三

    工作流程图:

    注意:

    1:  why num-=1没问题呢?这是因为动作太快(完成这个动作在切换的时间内)

    2: if sleep(1),现象会更明显,100个线程每一个一定都没有执行完就进行了切换,我们说过sleep就等效于IO阻塞,1s之内不会再切换回来,所以最后的结果一定是99.

     

    多个线程都在同时操作同一个共享资源,所以造成了资源破坏,怎么办呢?

    我们会想用join,但join会把整个线程给停住,造成了串行,失去了多线程的意义,而我们只需要把涉及到多线程操作公共数据的时候串行执行,而其它的逻辑还是并行的。

    可以通过同步锁来解决这种问题。

    import time
    import threading
    
    def addNum():
        global num
    
        lock.acquire()   #启用锁
        temp = num       #期间的指令执行完成后释放锁
        time.sleep(1)
        num=temp-1
        lock.release()   #释放锁
    
    num = 100
    thread_list = []
    lock=threading.Lock()  #声明一个同步锁
    for i in range(100):
        t = threading.Thread(target=addNum)
        t.start()
        thread_list.append(t)
    
    for t in thread_list:
        t.join()
    
    print('final num:', num )
    
    #final num: 0

    同步锁与GIL的关系:

    Python的线程在GIL的控制之下,线程之间,对整个python解释器,对python提供的C API的访问都是互斥的,这可以看作是Python内核级的互斥机制。但是这种互斥是我们不能控制的,我们还需要另外一种可控的互斥机制———用户级互斥。内核级通过互斥保护了内核的共享资源,同样,用户级互斥保护了用户程序中的共享资源。

    GIL 的作用是:对于一个解释器,只能有一个thread在执行bytecode。所以每时每刻只有一条bytecode在被执行。GIL保证了bytecode 这层面上是thread safe的。
    但是如果你有个操作比如 x += 1,这个操作需要多个bytecodes操作,在执行这个操作的多条bytecodes期间,由于CPU的切换,中途就换thread了,这样就出现了data races的情况了。
     
     

     线程死锁和递归锁

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

    import threading,time
    
    class myThread(threading.Thread):
        def doA(self):
            lockA.acquire()
            print(self.name,"gotlockA",time.ctime())
            time.sleep(3)
            lockB.acquire()
            print(self.name,"gotlockB",time.ctime())
            lockB.release()
            lockA.release()
    
        def doB(self):
            lockB.acquire()
            print(self.name,"gotlockB",time.ctime())
            time.sleep(2)
            lockA.acquire()
            print(self.name,"gotlockA",time.ctime())
            lockA.release()
            lockB.release()
        def run(self):
            self.doA()
            self.doB()
    if __name__=="__main__":
    
        lockA=threading.Lock()
        lockB=threading.Lock()
        threads=[]
        for i in range(5):
            threads.append(myThread())
        for t in threads:
            t.start()
        for t in threads:
            t.join()#等待线程结束,后面再讲。
    
    
    输出:
    Thread-1 gotlockA Thu Jun  7 09:28:24 2018
    Thread-1 gotlockB Thu Jun  7 09:28:27 2018
    Thread-1 gotlockB Thu Jun  7 09:28:27 2018
    Thread-2 gotlockA Thu Jun  7 09:28:27 2018
    死锁的例子

    解决方法:使用递归锁

    为了支持在同一线程中多次请求同一资源,python提供了“可重用锁”:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源.

    import threading,time
    
    class myThread(threading.Thread):
        def doA(self):
            lock.acquire()
            print(self.name,"gotlockA",time.ctime())
            time.sleep(3)
            lock.acquire()
            print(self.name,"gotlockB",time.ctime())
            lock.release()
            lock.release()
    
        def doB(self):
            lock.acquire()
            print(self.name,"gotlockB",time.ctime())
            time.sleep(2)
            lock.acquire()
            print(self.name,"gotlockA",time.ctime())
            lock.release()
            lock.release()
        def run(self):
            self.doA()
            self.doB()
    if __name__=="__main__":
    
        # lockA=threading.Lock()
        # lockB=threading.Lock()
        lock = threading.RLock()    #递归锁
        threads=[]
        for i in range(5):
            threads.append(myThread())
        for t in threads:
            t.start()
        for t in threads:
            t.join()#等待线程结束,后面再讲。
    
    #输出:
    # Thread-1 gotlockA Wed Mar 14 11:19:17 2018
    # Thread-1 gotlockB Wed Mar 14 11:19:20 2018
    # Thread-1 gotlockB Wed Mar 14 11:19:20 2018
    # Thread-1 gotlockA Wed Mar 14 11:19:22 2018
    # Thread-3 gotlockA Wed Mar 14 11:19:22 2018
    # Thread-3 gotlockB Wed Mar 14 11:19:25 2018
    # Thread-3 gotlockB Wed Mar 14 11:19:25 2018
    # Thread-3 gotlockA Wed Mar 14 11:19:27 2018
    # Thread-5 gotlockA Wed Mar 14 11:19:27 2018
    # Thread-5 gotlockB Wed Mar 14 11:19:30 2018
    # Thread-2 gotlockA Wed Mar 14 11:19:30 2018
    # Thread-2 gotlockB Wed Mar 14 11:19:33 2018
    # Thread-2 gotlockB Wed Mar 14 11:19:33 2018
    # Thread-2 gotlockA Wed Mar 14 11:19:35 2018
    # Thread-5 gotlockB Wed Mar 14 11:19:35 2018
    # Thread-5 gotlockA Wed Mar 14 11:19:37 2018
    # Thread-4 gotlockA Wed Mar 14 11:19:37 2018
    # Thread-4 gotlockB Wed Mar 14 11:19:40 2018
    # Thread-4 gotlockB Wed Mar 14 11:19:40 2018
    # Thread-4 gotlockA Wed Mar 14 11:19:42 2018
    #只有当计数器为0时,其它的进程才能进来
    案例一
    import time
    
    import threading
    
    class Account:
        def __init__(self, _id, balance):
            self.id = _id
            self.balance = balance
            self.lock = threading.RLock()
    
        def withdraw(self, amount):  #取钱
    
            with self.lock:
                self.balance -= amount
    
        def deposit(self, amount):  #存钱
            with self.lock:
                self.balance += amount
    
    
        def drawcash(self, amount):#lock.acquire中嵌套lock.acquire的场景
    
            with self.lock:
                interest=0.05
                count=amount+amount*interest
    
                self.withdraw(count)
    
    
    def transfer(_from, to, amount):  #转账
    
        #锁不可以加在这里 因为如果有外面的其它的函数在调用withdraw和deposit的情况下,同样是不安全的。所以锁应该加在函数内
         _from.withdraw(amount)
    
         to.deposit(amount)
    
    
    
    alex = Account('alex',1000)
    yuan = Account('yuan',1000)
    
    t1=threading.Thread(target = transfer, args = (alex,yuan, 100))
    t1.start()
    
    t2=threading.Thread(target = transfer, args = (yuan,alex, 200))
    t2.start()
    
    t1.join()
    t2.join()
    
    print('>>>',alex.balance)
    print('>>>',yuan.balance)
    案例二

    信号量(也是一种锁)

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

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

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

    同步锁和信号量的区别:

    同步锁:只能允许一个线程,最外层锁未释放,其它线程无法进入;

    信号量:可以并发执行线程,内部有一个计数器,每当调用acquire()时-1,调用release()时+1。

     实例:

    import threading,time
    class myThread(threading.Thread):
        def run(self):
            if semaphore.acquire():
                print(self.name)
                time.sleep(5)
                semaphore.release()
    if __name__=="__main__":
        semaphore=threading.Semaphore(5)   #创建一个信号量,参数就是定义最多允许多少个线程并行执行
        thrs=[]
        for i in range(23):
            thrs.append(myThread())
        for t in thrs:
            t.start()
    #输出:
    # Thread-1
    # Thread-2
    # Thread-3
    # Thread-4
    # Thread-5
    # Thread-6
    # Thread-8
    # Thread-9
    # Thread-7
    # Thread-10
    # Thread-11
    # Thread-12
    # Thread-14
    # Thread-13
    # Thread-15
    # Thread-18
    # Thread-16
    # Thread-17
    # Thread-19
    # Thread-20
    # Thread-21
    # Thread-22
    # Thread-23
    View Code

    条件变量同步(Condition),也是一种锁,可以实现线程间通信的作用

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

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

    wait():条件不满足时调用,线程会释放锁并进入等待阻塞;
    notify():条件创造后调用,通知等待池激活一个线程;
    notifyAll():条件创造后调用,通知等待池激活所有线程。

    实例:

    import threading,time
    from random import randint
    class Producer(threading.Thread):
        def run(self):
            global L
            while True:
                val=randint(0,100)
                print('生产者',self.name,":Append"+str(val),L)
                if lock_con.acquire():
                    L.append(val)
                    lock_con.notify()   #条件创造后调用,通知等待池激活一个线程
                    lock_con.release()
                time.sleep(3)
    class Consumer(threading.Thread):
        def run(self):
            global L
            while True:
                    lock_con.acquire()
                    if len(L)==0:
                        lock_con.wait()   #如果len(L)=0条件成立,消费者这个线程就会会释放锁并进入等待阻塞,即线程进入等待池
                    print('消费者',self.name,":Delete"+str(L[0]),L)
                    del L[0]
                    lock_con.release()
                    time.sleep(0.25)
    
    if __name__=="__main__":
    
        L=[]
        lock_con=threading.Condition()  #创建一个条件变量同步锁
        threads=[]
        for i in range(5):
            threads.append(Producer())
        threads.append(Consumer())   #这时会有6个线程对象
        for t in threads:
            t.start()
        for t in threads:
            t.join()
    条件变量同步锁

    条件同步(Event):不是锁

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

    event.isSet():返回event的状态值。

    event.wait():如果event.isSet()==false将阻塞线程。

    event.set():设置evevt的状态值为True,所有阻塞池的线程激活进入就绪状态,等待操作系统调度。

    event.clear():恢复event的状态值为flase。

     

     实例一:

    import threading,time
    class Boss(threading.Thread):
        def run(self):
            print("BOSS:今晚大家都要加班到22:00。")
            event.isSet() or event.set()
            time.sleep(5)
            print("BOSS:<22:00>可以下班了。")
            event.isSet() or event.set()
    class Worker(threading.Thread):
        def run(self):
            event.wait()   #开始event.isSet()==False,线程阻塞
            print("Worker:哎……命苦啊!")
            time.sleep(0.25)
            event.clear()
            event.wait()
            print("Worker:OhYeah!")
    if __name__=="__main__":
        event=threading.Event()
        threads=[]
        for i in range(5):
            threads.append(Worker())
        threads.append(Boss())
        for t in threads:
            t.start()
        for t in threads:
            t.join()
    #通过event的标准位,进行线程间通信
    #输出
    # BOSS:今晚大家都要加班到22:00。
    # Worker:哎……命苦啊!
    # Worker:哎……命苦啊!
    # Worker:哎……命苦啊!
    # Worker:哎……命苦啊!
    # Worker:哎……命苦啊!
    # BOSS:<22:00>可以下班了。
    # Worker:OhYeah!
    # Worker:OhYeah!
    # Worker:OhYeah!
    # Worker:OhYeah!
    # Worker:OhYeah!
    View Code

    实例二:

    import threading,time
    import random
    def light():
        if not event.isSet():
            event.set() #wait就不阻塞 #绿灯状态
        count = 0
        while True:
            if count < 10:
                print('33[42;1m--green light on---33[0m')
            elif count <13:
                print('33[43;1m--yellow light on---33[0m')
            elif count <20:
                if event.isSet():
                    event.clear()
                print('33[41;1m--red light on---33[0m')
            else:
                count = 0
                event.set() #打开绿灯
            time.sleep(1)
            count +=1
    def car(n):
        while 1:
            time.sleep(random.randrange(10))
            if  event.isSet(): #绿灯
                print("car [%s] is running.." % n)
            else:
                print("car [%s] is waiting for the red light.." %n)
    if __name__ == '__main__':
        event = threading.Event()
        Light = threading.Thread(target=light)
        Light.start()
        for i in range(3):
            t = threading.Thread(target=car,args=(i,))
            t.start()
    View Code

    多线程利器:队列

    队列中有一把锁,保证数据安全,保证一次只能有一个线程往队列中存放数据,一次也只能有一个线程往队列中取数据。

    队列中的方法:

    创建一个“队列”对象
    import queue
    q = queue.Queue(maxsize = 10)
    queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限,默认值为0。
    
    将一个值放入队列中
    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() 实际上意味着等到队列为空,再执行别的操作

    队列实例一:

    import threading,queue
    from time import sleep
    from random import randint
    class Production(threading.Thread):
        def run(self):
            while True:
                r=randint(0,100)
                q.put(r)
                print("生产出来%s号包子"%r)
                sleep(1)
    class Proces(threading.Thread):
        def run(self):
            while True:
                re=q.get()
                print("吃掉%s号包子"%re)
    if __name__=="__main__":
        q=queue.Queue(10)
        threads=[Production(),Production(),Production(),Proces()]
        for t in threads:
            t.start()
    View Code

    队列实例二:

    import time,random
    import queue,threading
    q = queue.Queue()
    def Producer(name):
      count = 0
      while count <20:
        time.sleep(random.randrange(3))
        q.put(count)
        print('Producer %s has produced %s baozi..' %(name, count))
        count +=1
    def Consumer(name):
      count = 0
      while count <20:
        time.sleep(random.randrange(4))
        if not q.empty():
            data = q.get()
            print(data)
            print('33[32;1mConsumer %s has eat %s baozi...33[0m' %(name, data))
        else:
            print("-----no baozi anymore----")
        count +=1
    p1 = threading.Thread(target=Producer, args=('A',))
    c1 = threading.Thread(target=Consumer, args=('B',))
    p1.start()
    c1.start()
    View Code

    队列实例三:

    #实现一个线程不断生成一个随机数到一个队列中(考虑使用Queue这个模块)
    # 实现一个线程从上面的队列里面不断的取出奇数
    # 实现另外一个线程从上面的队列里面不断取出偶数
    
    import random,threading,time
    from queue import Queue
    #Producer thread
    class Producer(threading.Thread):
      def __init__(self, t_name, queue):
        threading.Thread.__init__(self,name=t_name)
        self.data=queue
      def run(self):
        for i in range(10):  #随机产生10个数字 ,可以修改为任意大小
          randomnum=random.randint(1,99)
          print ("%s: %s is producing %d to the queue!" % (time.ctime(), self.getName(), randomnum))
          self.data.put(randomnum) #将数据依次存入队列
          time.sleep(1)
        print ("%s: %s finished!" %(time.ctime(), self.getName()))
    
    #Consumer thread
    class Consumer_even(threading.Thread):
      def __init__(self,t_name,queue):
        threading.Thread.__init__(self,name=t_name)
        self.data=queue
      def run(self):
        while 1:
          try:
            val_even = self.data.get(1,5) #get(self, block=True, timeout=None) ,1就是阻塞等待,5是超时5秒
            if val_even%2==0:
              print ("%s: %s is consuming. %d in the queue is consumed!" % (time.ctime(),self.getName(),val_even))
              time.sleep(2)
            else:
              self.data.put(val_even)
              time.sleep(2)
          except:   #等待输入,超过5秒 就报异常
            print ("%s: %s finished!" %(time.ctime(),self.getName()))
            break
    class Consumer_odd(threading.Thread):
      def __init__(self,t_name,queue):
        threading.Thread.__init__(self, name=t_name)
        self.data=queue
      def run(self):
        while 1:
          try:
            val_odd = self.data.get(1,5)
            if val_odd%2!=0:
              print ("%s: %s is consuming. %d in the queue is consumed!" % (time.ctime(), self.getName(), val_odd))
              time.sleep(2)
            else:
              self.data.put(val_odd)
              time.sleep(2)
          except:
            print ("%s: %s finished!" % (time.ctime(), self.getName()))
            break
    #Main thread
    def main():
      queue = Queue()
      producer = Producer('Pro.', queue)
      consumer_even = Consumer_even('Con_even.', queue)
      consumer_odd = Consumer_odd('Con_odd.',queue)
      producer.start()
      consumer_even.start()
      consumer_odd.start()
      producer.join()
      consumer_even.join()
      consumer_odd.join()
      print ('All threads terminate!')
    
    if __name__ == '__main__':
      main()
    View Code

    注意:列表是线程不安全的

    import threading,time
    
    li=[1,2,3,4,5]
    
    def pri():
        while li:
            a=li[-1]
            print(a)
            time.sleep(1)
            try:
                li.remove(a)
            except:
                print('----',a)
    
    t1=threading.Thread(target=pri,args=())
    t1.start()
    t2=threading.Thread(target=pri,args=())
    t2.start()
    
    #两个有可能同时拿到的时同一个数字,而队列不会出现这种情况
    View Code

     

     

     

     

     

  • 相关阅读:
    面试汇总
    Windows 环境下的 protoc 安装(转)
    Hbase 元数据一致性检查(转)
    Slow ReadProcessor&amp;Error Slow BlockReceiver错误日志分析(转)
    Hbase CMS GC 调优。
    [技术]排查线上问题
    crontab 误删恢复
    Hbase balancer RSgroup shell 脚本
    Hbase运维手册(1)
    Presto JVM.config
  • 原文地址:https://www.cnblogs.com/lriwu/p/8550596.html
Copyright © 2020-2023  润新知