线程

作者: 小吉头 | 来源:发表于2020-08-15 21:44 被阅读0次

    什么是线程

    代码由静态变为动态运行的时候,负责执行代码的东西就叫线程

    如何使用线程

    一、通过threading.Thread类初始化


    当主线程执行了t1.start(),此时创建子线程t1,需要执行的函数是sing。然后主线程和子线程在操作系统的时间片轮转下随机执行。当又轮到主线程时,t2.start()创建了子线程t2,此时主线程、子线程t1、子线程t2在时间片轮转下随机执行。
    注意:
    1、当主线程下面没有代码时,必须等待子线程结束,因为主线程一旦结束整个程序就结束了
    2、当创建了线程对象t1、t2,此时没有创建子线程,只有调用start()才会有子线程,执行的代码就是target指向的函数,当子线程对应的target函数执行完成,子线程就结束了

    join()的作用:

    join()会等待子线程执行完成,再执行后面的语句

    import threading
    import time
    
    def sing():
        time.sleep(1)
        print('sing')
    
    
    def dance():
        time.sleep(2)
        print('dance')
    
    if __name__ == "__main__":
        t1 = threading.Thread(target=sing)
        t2 = threading.Thread(target=dance)
        t1.start()
        t2.start()
        t1.join()
        t2.join()
        print("我是主线程")
    
    
    >>>sing
    >>>dance
    >>>我是主线程
    

    setDaemon()的作用

    默认情况下,主线程执行完会等待子线程执行完再退出,子线程如果设置了setDaemon(True),主线程无需等待,直接退出,子线程也就跟着结束了

    import threading
    import time
    
    def sing():
        time.sleep(1)
        print('sing')
    
    
    def dance():
        time.sleep(2)
        print('dance')
    
    if __name__ == "__main__":
        t1 = threading.Thread(target=sing)
        t2 = threading.Thread(target=dance)
        t1.setDaemon(True)
        t2.setDaemon(True)
        t1.start()
        t2.start()
        print("我是主线程")
    
    >>>我是主线程
    

    二、继承自threading.Thread类

    import threading
    import time
    
    class Sing(threading.Thread):
        def run(self):
            while True:
                print("sing")
                time.sleep(1)
    
    
    class Dance(threading.Thread):
        def run(self):
            while True:
                print("dance")
                time.sleep(1)
    
    if __name__ == "__main__":
        t1 = Sing()
        t2 = Dance()
        t1.start()
        t2.start()
    

    流程跟上面的一样,只是继承了threading.Thread类,需要重写run()方法,最终还是通过start()去创建子线程并执行run()方法

    线程间通信

    一、主线程和子线程共享全局变量

    共享全局变量的目的是让多线程之间可以协作完成任务。假设音乐播放器开启一个子线程下载歌曲,歌曲内容放到全局变量中,主线程从全局变量读取内容播放,形成了边下载边放歌的多任务

    注意:下图示例代码中,如果只是获取全局变量的值,是不需要加global的,如果要修改全局变量,得看全局变量是不是可变类型,如果是可变,可以不加global直接修改,如果不可变,必须要加global才能修改



    python中哪些可变,哪些不可变,以及在内存中是如何存储的,后面的文章会介绍

    多线程同时修改全局变量容易出问题:

    如下代码,两个线程对全局变量同时修改:

    import threading
    import time
    
    num = 0
    
    def test():
        global num
        for i in range(100000):
            num += 1
        print('t1结束')
    
    def test1():
        global num
        for i in range(100000):
            num += 1
        print('t2结束')
    
    if __name__ == "__main__":
        t1 = threading.Thread(target=test)
        t2 = threading.Thread(target=test1)
        t1.start()
        t2.start()
        time.sleep(1)
        print(num)
    >>>t1结束
    >>>t2结束
    >>>176111
    

    理论结果应该是20000,实际只有17万多,为什么会出现这种情况?
    查看字节码:

    import dis
    
    num = 0
    
    def numAdd():
        global num
        num += 1
    
    dis.dis(numAdd)
    
    >>>             0 LOAD_GLOBAL              0 (num)
                    3 LOAD_CONST               1 (1)
                    6 INPLACE_ADD
                    7 STORE_GLOBAL             0 (num)
                   10 LOAD_CONST               0 (None)
                   13 RETURN_VALUE
    
    
    

    num +=1被翻译成了前4条指令,当num=0时,两个线程同时执行,t1如果还没有来得及保存,由于时间片轮转,就先去执行了t2,此时t2的数据保存成功,num=1,又轮到t1继续执行,此时t1开始保存之前的值,num还是等于1。因此会出现跟理论值不符的情况,这种情况下多线程是不安全的。

    二、通过队列实现通信

    队列是线程安全的,具体原理需要再查看下队列的底层实现

    import threading
    from queue import Queue
    
    def download_data(q):
        data = [1,2,3,4]
        #向队列中写入数据
        for temp in data:
            q.put(temp)
    
    
    def deal_data(q):
        #从队列中获取数据
        resList = list()
        while True:
            data = q.get()
            resList.append(data)
            if q.empty():
                break
        print(resList)
    
    def main():
        #1、创建一个队列
        q = Queue()
        #2、创建多个进程,将队列的引用传递过去
        t1 = threading.Thread(target=download_data,args=(q,))
        t2 = threading.Thread(target=deal_data,args=(q,))
        t1.start()
        t2.start()
    
    if __name__ == "__main__":
        main()
    

    相关文章

      网友评论

          本文标题:线程

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