美文网首页
1.18python之多线程

1.18python之多线程

作者: Benedict清水 | 来源:发表于2020-10-15 13:50 被阅读0次

    一、使用threading模块实现线程的创建

    实例1

    import threading
    from time import ctime, sleep
    
    
    def target():
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
        sleep(5)
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
    
    
    if __name__ == "__main__":
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
        t = threading.Thread(target=target)
        t.start()
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
    

    输出结果:

    The current threading MainThread---Wed Oct 14 18:32:19 2020 is running
    The current threading Thread-1---Wed Oct 14 18:32:19 2020 is running
    The current threading MainThread---Wed Oct 14 18:32:19 2020 is running
    The current threading Thread-1---Wed Oct 14 18:32:24 2020 is running
    
    Process finished with exit code 0
    

    import threading
    首先导入threading 模块,这是使用多线程的前提。
    t = threading.Thread(target=target)
    创建线程t,使用threading.Thread()方法。
    t.start()
    开始线程活动。
    使用threading.current_thread()可以查看到当前线程的信息。
    从输出结果可以看到在线程Thread-1结束前MainThread已经结束了,但并没有杀死子线程Thread-1。

    实例2

    import threading
    from time import ctime, sleep
    
    
    def target():
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
        sleep(5)
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
    
    
    if __name__ == "__main__":
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
        t = threading.Thread(target=target)
        t.start()
        t.join()
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
    

    t.join()
    join()的作用是,在子线程完成运行之前,这个子线程的父线程将一直被阻塞。Python中,默认情况下,如果不加join()语句,那么主线程不会等到当前线程结束才结束,但却不会立即杀死该线程。如上面的输出结果所示。
    输出结果:

    The current threading MainThread---Wed Oct 14 18:40:42 2020 is running
    The current threading Thread-1---Wed Oct 14 18:40:42 2020 is running
    The current threading Thread-1---Wed Oct 14 18:40:47 2020 is running
    The current threading MainThread---Wed Oct 14 18:40:47 2020 is running
    
    Process finished with exit code 0
    

    实例3

    import threading
    from time import ctime, sleep
    
    
    def target():
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
        sleep(5)
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
    
    
    if __name__ == "__main__":
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
        t = threading.Thread(target=target)
        t.setDaemon(True)
        t.start()
        t.join()
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
    

    输出结果:

    The current threading MainThread---Thu Oct 15 13:07:04 2020 is running
    The current threading Thread-1---Thu Oct 15 13:07:04 2020 is running
    The current threading Thread-1---Thu Oct 15 13:07:09 2020 is running
    The current threading MainThread---Thu Oct 15 13:07:09 2020 is running
    
    Process finished with exit code 0
    

    t.setDaemon(True)
    t.setDaemon(True)将线程声明为守护线程,必须在start() 方法调用之前设置,如果不设置为守护线程程序会被无限挂起。如果当前python线程是守护线程,那么意味着这个线程是“不重要”的,“不重要”意味着如果他的主进程结束了但该守护线程没有运行完,守护进程就会被强制结束。如果线程是非守护线程,那么父进程只有等到守护线程运行完毕后才能结束

    import threading
    from time import ctime, sleep
    
    
    def target():
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
        sleep(5)
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
    
    
    if __name__ == "__main__":
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
        t = threading.Thread(target=target)
        t.setDaemon(True)
        t.start()
        # t.join()
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
    

    输出结果:

    The current threading MainThread---Thu Oct 15 13:08:38 2020 is running
    The current threading Thread-1---Thu Oct 15 13:08:38 2020 is running
    The current threading MainThread---Thu Oct 15 13:08:38 2020 is running
    
    Process finished with exit code 0
    

    如果为线程实例添加t.setDaemon(True)之后,如果不加join语句,那么当主线程结束之后,会杀死子线程。

    二、使用threading模块实现多线程的创建

    1、函数的方式创建

    import threading
    from time import ctime, sleep
    
    
    def code():
        print("I'm coding. {}---{}".format(ctime(), threading.current_thread().name))
        sleep(5)
    
    
    def draw():
        print("I'm drawing. {}---{}".format(ctime(), threading.current_thread().name))
        sleep(5)
    
    
    if __name__ == "__main__":
        threads = []
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
        t1 = threading.Thread(target=code)
        threads.append(t1)
        t2 = threading.Thread(target=draw)
        threads.append(t2)
        for t in threads:
            t.setDaemon(True)
            t.start()
        t.join()
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
    

    输出结果:

    The current threading MainThread---Thu Oct 15 13:35:06 2020 is running
    I'm coding. Thu Oct 15 13:35:06 2020---Thread-1
    I'm drawing. Thu Oct 15 13:35:06 2020---Thread-2
    The current threading MainThread---Thu Oct 15 13:35:11 2020 is running
    
    Process finished with exit code 0
    

    给线程传递参数

    import threading
    from time import ctime, sleep
    
    
    def code(arg):
        print("I'm coding.{}---{}---{}".format(arg, ctime(), threading.current_thread().name))
        sleep(5)
    
    
    def draw(arg):
        print("I'm drawing.{}----{}---{}".format(arg, ctime(), threading.current_thread().name))
        sleep(5)
    
    
    if __name__ == "__main__":
        threads = []
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
        t1 = threading.Thread(target=code, args=('敲代码',))
        threads.append(t1)
        t2 = threading.Thread(target=draw, args=('画画',))
        threads.append(t2)
        for t in threads:
            t.setDaemon(True)
            t.start()
        t.join()
        print("The current threading {}---{} is running".format(threading.current_thread().name, ctime()))
    

    输出结果:

    The current threading MainThread---Thu Oct 15 13:39:49 2020 is running
    I'm coding.敲代码---Thu Oct 15 13:39:49 2020---Thread-1
    I'm drawing.画画----Thu Oct 15 13:39:49 2020---Thread-2
    The current threading MainThread---Thu Oct 15 13:39:54 2020 is running
    
    Process finished with exit code 0
    

    2.类的方式创建线程

    继承自threading.Thread类
    为了让线程代码更好的封装,可以使用threading模块的下的Thread类,继承自这个类,然后实现run方法,线程就会自动运行run方法中的代码。

    import threading
    from time import ctime, sleep
    
    
    class CodingThread(threading.Thread):
        def run(self):
            print("I'm coding.{}---{}".format(ctime(), threading.current_thread().name))
            sleep(5)
    
    
    class DrawingThread(threading.Thread):
        def run(self):
            print("I'm drawing.{}---{}".format(ctime(), threading.current_thread().name))
            sleep(5)
    
    
    def multi_thread():
        t1 = CodingThread()
        t2 = DrawingThread()
    
        print(threading.enumerate())
        t1.start()
        print(threading.enumerate())
        t2.start()
        print(threading.enumerate())
    
    
    if __name__ == "__main__":
        multi_thread()
    

    输出结果

    [<_MainThread(MainThread, started 4403457344)>]
    I'm coding.Thu Oct 15 13:45:06 2020---Thread-1
    [<_MainThread(MainThread, started 4403457344)>, <CodingThread(Thread-1, started 123145444630528)>]
    I'm drawing.Thu Oct 15 13:45:06 2020---Thread-2
    [<_MainThread(MainThread, started 4403457344)>, <CodingThread(Thread-1, started 123145444630528)>, <DrawingThread(Thread-2, started 123145461420032)>]
    
    Process finished with exit code 0
    

    三、多线程共享全局变量以及锁机制

    1、多线程共享变量的问题

    对于多线程来说,最大的特点就是线程之间可以共享数据,线程的执行又是无序的,那么共享数据就会出现多线程同时更改一个变量,使用同样的资源,而出现死锁、数据错乱等情况。

    import threading
    
    
    value = 0
    
    
    class AddValueThread(threading.Thread):
        def run(self):
            global value
            for x in range(1000000):
                value += 1
            print("{}的值是{}".format(threading.current_thread().name, value))
    
    
    def multi_thread():
        for i in range(2):
            t = AddValueThread()
            t.start()
    
    
    if __name__ == "__main__":
        multi_thread()
    

    输出结果:

    Thread-1的值是1214452
    Thread-2的值是1393110
    
    Process finished with exit code 0
    

    这个结果是错误的,正确的结果应该是:

    Thread-1的值是1000000
    Thread-2的值是2000000
    

    由于两条线程同时对value操作,所以这里就出现数据错误了

    2、线程锁和ThreadLocal

    (1)线程锁

    为了解决以上使用共享变量的问题。threading提供了一个Lock类,这个类可以在某个线程访问某个变量的时候加锁,其他线程就进不来,直到当前进程处理完成后,释放了锁,其他线程才能进来进行处理。当访问某个资源之前,用Lock.acquire()锁住资源,访问之后,用Lock.release()释放资源。

    import threading
    
    value = 0
    gLock = threading.Lock()
    
    
    class AddValueThread(threading.Thread):
        def run(self):
            global value
            gLock.acquire()
            for x in range(1000000):
                value += 1
            gLock.release()
            print("{}的值是{}".format(threading.current_thread().name, value))
    
    
    def multi_thread():
        for i in range(2):
            t = AddValueThread()
            t.start()
    
    
    if __name__ == "__main__":
        multi_thread()
    

    输出结果:

    Thread-1的值是1000000
    Thread-2的值是2000000
    
    Process finished with exit code 0
    
    (2)、ThreadLocal

    介绍完线程锁,接下来出场的是ThreadLocal。当不想将变量共享给其他线程时,可以使用局部变量,但在函数中定义局部变量会使得在函数之间传递特别麻烦。ThreadLocal是非常牛逼的东西,它解决了全局变量需要枷锁,局部变量传递麻烦的两个问题。通过在线程中定义:
    local_school = threading.local()
    此时这个local_school就变成了一个全局变量,但这个全局变量只在该线程中为全局变量,对于其他线程来说是局部变量,别的线程不可更改。

    def process_thread(name): # 绑定ThreadLocal的student: 
                 local_school.student = name
    

    这个student属性只有本线程可以修改,别的线程不可以。代码:

    import threading
    
    value = 0
    gLocal = threading.local()
    
    
    class AddValueThread(threading.Thread):
        def run(self):
            gLocal.value = 0
            for x in range(1000000):
                gLocal.value += 1
            print("{}的值是{}".format(threading.current_thread().name, gLocal.value))
    
    
    def multi_thread():
        for i in range(2):
            t = AddValueThread()
            t.start()
    
    
    if __name__ == "__main__":
        multi_thread()
    

    输出结果:

    Thread-1的值是1000000
    Thread-2的值是1000000
    
    Process finished with exit code 0
    

    四、生产者和消费者模式

    (1)、Lock版

    生产者线程专门用来生产一些数据,然后存放到中间变量中,消费者再从中间的变量中取出数据进行消费。中间变量经常是一些全局变量,所以需要使用锁来保证数据完整性。

    import threading
    import random
    import time
    
    gMoney = 1000
    gTimes = 0
    gLock = threading.Lock()
    
    
    class Producer(threading.Thread):
        def run(self):
            global gMoney
            global gTimes
            while True:
                money = random.randint(100, 1000)
                gLock.acquire()
                if gTimes >= 3:
                    gLock.release()
                    break
                gMoney += money
                print("{}当前存入{}元钱,剩余{}元钱".format(threading.current_thread(), money, gMoney))
                gTimes += 1
                time.sleep(0.5)
                gLock.release()
    
    
    class Consumer(threading.Thread):
        def run(self):
            global gMoney
            global gTimes
            while True:
                money = random.randint(100, 500)
                gLock.acquire()
                if gMoney > money:
                    gMoney -= money
                    print("{}当前取出{}元钱,剩余{}元钱".format(threading.current_thread(), money, gMoney))
                    time.sleep(0.5)
                else:
                    if gTimes >= 3:
                        gLock.release()
                        break
                    print("{}当前想取出{}元钱,剩余{}元钱,不足!".format(threading.current_thread(), money, gMoney))
                gLock.release()
    
    
    def multi_thread():
        for i in range(2):
            Consumer(name="消费者线程{}".format(i)).start()
        for j in range(2):
            Producer(name="生产者线程{}".format(j)).start()
    
    
    if __name__ == "__main__":
        multi_thread()
    

    输出结果:

    <Consumer(消费者线程0, started 123145324752896)>当前取出128元钱,剩余872元钱
    <Consumer(消费者线程1, started 123145341542400)>当前取出420元钱,剩余452元钱
    <Producer(生产者线程0, started 123145358331904)>当前存入997元钱,剩余1449元钱
    <Producer(生产者线程1, started 123145375121408)>当前存入700元钱,剩余2149元钱
    <Producer(生产者线程1, started 123145375121408)>当前存入984元钱,剩余3133元钱
    <Consumer(消费者线程1, started 123145341542400)>当前取出221元钱,剩余2912元钱
    <Consumer(消费者线程0, started 123145324752896)>当前取出313元钱,剩余2599元钱
    <Consumer(消费者线程1, started 123145341542400)>当前取出189元钱,剩余2410元钱
    <Consumer(消费者线程0, started 123145324752896)>当前取出356元钱,剩余2054元钱
    <Consumer(消费者线程1, started 123145341542400)>当前取出109元钱,剩余1945元钱
    <Consumer(消费者线程0, started 123145324752896)>当前取出418元钱,剩余1527元钱
    <Consumer(消费者线程1, started 123145341542400)>当前取出381元钱,剩余1146元钱
    <Consumer(消费者线程0, started 123145324752896)>当前取出416元钱,剩余730元钱
    <Consumer(消费者线程0, started 123145324752896)>当前取出166元钱,剩余564元钱
    <Consumer(消费者线程0, started 123145324752896)>当前取出111元钱,剩余453元钱
    <Consumer(消费者线程1, started 123145341542400)>当前取出384元钱,剩余69元钱
    <Consumer(消费者线程0, started 123145324752896)>当前想取出415元钱,剩余69元钱,不足!
    <Consumer(消费者线程1, started 123145341542400)>当前想取出100元钱,剩余69元钱,不足!
    
    Process finished with exit code 0
    

    (2)、Condition版

    LOCK版本的生产者和消费者存在一个不足,在消费者中总是通过while True死循环并且上锁的方式判断资源够不够。上锁是一个很耗费cpu资源的行为。因此这种方式不是最好的。还有一种更好的方式是使用threading.Condition来实现。threading.Condition消费者可以在没有数据的时候处于阻塞等待状态。生产者一旦有合适的数据,还可以使用notify相关的函数来通知处于等待阻塞状态的线程。这样就可以避免一些无用的上锁、解锁的操作。
    threading.Condition类似threading.Lock,可以在修改全局数据的时候进行上锁,也可以在修改完毕后进行解锁。
    acquire:上锁
    release:解锁
    wait:将当前线程处于等待状态,并且会释放锁。可以被其他线程使用notify和notify_all函数唤醒。被唤醒后继续等待上锁,上锁后继续执行下面的代码。
    notify:通知某个正在等待的线程,默认是第1个等待的线程。
    notify_all:通知所有正在等待的线程。
    注意: notify和notify_all不会释放锁。并且需要在release之前调用。

    import threading
    import random
    import time
    
    gMoney = 1000
    gCondition = threading.Condition()  # 锁
    gTimes = 0
    
    
    class Producer(threading.Thread):
        def run(self):
            global gMoney
            global gTimes
            while True:
                money = random.randint(100, 1000)
                gCondition.acquire()
                if gTimes >= 3:
                    gCondition.release()
                    break
                gMoney += money
                print("{}当前存入{}元钱,剩余{}元钱".format(threading.current_thread(), money, gMoney))
                gTimes += 1
                gCondition.notify_all()
                gCondition.release()
                time.sleep(0.5)
    
    
    class Consumer(threading.Thread):
        def run(self):
            global gMoney
            global gTimes
            while True:
                money = random.randint(100, 500)
                gCondition.acquire()
                while gMoney < money:
                    print("{}准备消费{}元钱,还剩{}元钱,余额不足!".format(threading.current_thread(), money, gMoney))
                    if gTimes >= 3:
                        gCondition.release()
                        return
                    gCondition.wait()
                gMoney -= money
                print("{}消费了{}元钱,剩余{}元钱".format(threading.current_thread(), money, gMoney))
                gCondition.release()
                time.sleep(0.5)
    
    
    def multi_thread():
        for i in range(2):
            Consumer(name="消费者线程{}".format(i)).start()
        for j in range(2):
            Producer(name="生产者线程{}".format(j)).start()
    
    
    if __name__ == "__main__":
        multi_thread()
    

    输出结果:

    <Consumer(消费者线程0, started 123145357996032)>消费了273元钱,剩余727元钱
    <Consumer(消费者线程1, started 123145374785536)>消费了470元钱,剩余257元钱
    <Producer(生产者线程0, started 123145391575040)>当前存入181元钱,剩余438元钱
    <Producer(生产者线程1, started 123145408364544)>当前存入464元钱,剩余902元钱
    <Consumer(消费者线程0, started 123145357996032)>消费了455元钱,剩余447元钱
    <Producer(生产者线程0, started 123145391575040)>当前存入677元钱,剩余1124元钱
    <Consumer(消费者线程1, started 123145374785536)>消费了400元钱,剩余724元钱
    <Consumer(消费者线程0, started 123145357996032)>消费了485元钱,剩余239元钱
    <Consumer(消费者线程1, started 123145374785536)>消费了159元钱,剩余80元钱
    <Consumer(消费者线程0, started 123145357996032)>准备消费325元钱,还剩80元钱,余额不足!
    <Consumer(消费者线程1, started 123145374785536)>准备消费229元钱,还剩80元钱,余额不足!
    
    Process finished with exit code 0
    

    相关文章

      网友评论

          本文标题:1.18python之多线程

          本文链接:https://www.haomeiwen.com/subject/jxwwpktx.html