线程

作者: furuiyang | 来源:发表于2018-04-24 19:15 被阅读0次

    什么是线程?

    1.一个程序运行起来至少有一个进程,一个进程至少有一个线程
    2.处理器cpu分配给线程,即cpu真正运行的是线程
    3.分配cpu资源给线程时,是通过时间片轮训方式进行的
    4.进程是操作系统分配程序执行资源的单位,而线程是进程的一个实体,
     是CPU调度和分配的单位。
    

    开启多线程

    python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用,通过threading模块可以创建线程。

    import threading
    import time
    
    
    def download_music():
        """模拟下载歌曲,需要5秒钟下载完成"""
        for i in range(5):
            time.sleep(1)  # 休眠1秒
            print("---正在下载歌曲%d---" % i)
    
    
    def play_music():
        """模拟播放歌曲,需要5秒钟下载完成"""
        for i in range(5):
            time.sleep(1)  # 休眠1秒
            print("---正在播放歌曲%d---" % i)
    
    
    def main():
        # 创建线程对象t1
        # target: 指向新开启的线程要执行的代码
        t1 = threading.Thread(target=download_music)
        t2 = threading.Thread(target=play_music)
    
        t1.start()  # 启动线程,既然线程开始执行
        t2.start()
    
    
    if __name__ == '__main__':
        main()
    

    运行结果:

    多线程听歌程序运行结果.png

    说明

    1. 可以明显看出使用了多线程并发的操作,花费时间要短很多;
    2. 当调用start()时,才会真正的执行线程,执行线程中的代码。

    线程何时开始和结束

    1.子线程何时开启,何时运行
       当调用thread.start()时 开启线程,再运行线程的代码
    2.子线程何时结束
       子线程把target指向的函数中的语句执行完毕后,立即结束当前子线程
    3.查看当前线程数量
       通过threading.enumerate()可枚举当前运行的所有线程
    4.主线程何时结束
       所有子线程执行完毕后,主线程才结束
    
    import threading
    import time
    
    def test1():
        for i in range(5):
            time.sleep(1)
            print('---子线程1---%d' %i)
            print('子线程1中查看线程情况',threading.enumerate())
    
    def test2():
        for i in range(10):
            time.sleep(1)
            print('---子线程2---%d' %i)
            print('子线程2中查看线程情况',threading.enumerate())
    
    def main():
        print('创建线程之前的线程情况',threading.enumerate())
        t1 = threading.Thread(target=test1)
        t2 = threading.Thread(target=test2)
        time.sleep(1)
        print('创建线程之后的线程执行情况',threading.enumerate())
        t1.start()
        t2.start()
        time.sleep(1)
        print('调用了thread.start()之后的线程情况',threading.enumerate())
        t2.join()  # 当t2线程执行完毕之后 在执行后续的代码
        print('列表目前的线程',threading.enumerate())
    
    if __name__ == '__main__':
        main()
    
    

    运行结果略,可以自行运行显示。

    线程执行过程中的注意点

    线程执行代码的封装

    通过上一小节,能够看出,通过使用threading模块能完成多任务的程序开发,为了让每个线程的封装性更完美,所以使用threading模块时,往往会定义一个新的子类class,只要继承threading.Thread就可以了,然后重写run方法。

    import threading
    import time
    
    class MyThread(threading.Thread):
        def run(self):
            for i in range(5):
                time.sleep(1)
                msg = "I'm "+ self.name + '@' + str(i)
                print(msg)
    
    if __name__ == '__main__':
        t1 = MyThread()
        t1.start()
    

    执行结果:

    线程执行代码的封装.png

    说明:

    python的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。而创建自己的线程实例后,通过Thread类的start方法,可以启动该线程,交给python虚拟机进行调度,当该线程获得执行的机会时,就会调用run方法执行线程。

    线程的执行顺序

    # coding:utf-8
    import threading
    import time
    
    class MyThread(threading.Thread):
        def run(self):
            for i in range(3):
                time.sleep(1)
                msg = "I'm " + self.name + '@' + str(i)
                print(msg)
    
    def test():
        for i in range(5):
            t = MyThread()
            t.start()
    
    if __name__ == '__main__':
        test()
    

    执行结果:

    线程的执行顺序.png

    说明:

    从代码和执行结果我们可以看出,多线程程序的执行顺序是不确定的。当执行到sleep语句时,线程将被阻塞(Blocked),到sleep结束后,线程进入就绪(Runnable)状态,等待调度。而线程调度将自行选择一个线程执行。上面的代码中只能保证每个线程都运行完整个run函数,但是线程的启动顺序、run函数中每次循环的执行顺序都不能确定。

    总结

    1 每个线程默认有一个名字,尽管上面的例子中没有指定线程对象的name,但是python会自动为线程指定一个名字。
    2 当线程的run()方法结束时该线程完成。
    3 无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。

    多线程共享全局变量

    总结

    在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据
    缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)

    示例代码

    from threading import Thread
    import time
    
    g_num = 100
    
    def work1():
        global g_num
        for i in range(3):
            g_num += 1
        print('---in work1,g_num is %d---' %g_num)
    
    def work2():
        global g_num
        print('---in work2,g_num is %d---' %g_num)
    
    print('---线程创建之前g_num is %d---' %g_num)
    
    t1 = Thread(target=work1)
    t1.start()
    
    # 延时一会 办证t1线程中的事情做完
    time.sleep(1)
    
    t2 = Thread(target=work2)
    t2.start()
    

    运行结果

    11.png

    列表当做实参传递到线程中

    from threading import Thread
    import time
    
    def work1(nums):
        nums.append(44)
        print('---in work1---',nums)
    
    def work2(nums):
        # 延时一会,保证t1线程中的事情做完
        time.sleep(1)
        print('---in work2---',nums)
    
    g_nums = [11,22,33]
    
    t1 = Thread(target=work1,args=(g_nums,))
    t1.start()
    
    t2 = Thread(target=work2,args=(g_nums,))
    t2.start()
    
    22.png

    多线程-共享全局变量的问题

    多线程开发可能会遇到的问题

    假设两个线程t1和t2都要对全局变量g_num(默认是0)进行加1运算,t1和t2都各对g_num加10次,g_num的最终的结果应该为20。
    但是由于是多线程同时操作,有可能出现下面情况:
    1.在g_num=0时,t1取得g_num=0。此时系统把t1调度为”sleeping”状态,把t2转换为”running”状态,t2也获得g_num=0
    2.然后t2对得到的值进行加1并赋给g_num,使得g_num=1
    3.然后系统又把t2调度为”sleeping”,把t1转为”running”。线程t1又把它之前得到的0加1后赋值给g_num。
    4.这样导致虽然t1和t2都对g_num加1,但结果仍然是g_num=1

    进行一个测试代码:

    import threading
    import time
    
    g_num = 0
    
    def work1(num):
        global g_num
        for i in range(num):
            g_num += 1
        print('---in work1,g_num is %d---' %g_num)
    
    def work2(num):
        global g_num
        for i in range(num):
            g_num += 1
        print('---in work2,g_num is %d---' %g_num)
    
    print('---线程创建之前g_num is %d---'%g_num)
    
    t1 = threading.Thread(target=work1,args=(100,))
    t1.start()
    
    t2 = threading.Thread(target=work2,args=(100,))
    t2.start()
    
    while len(threading.enumerate()) != 1:
        time.sleep(1)
    
    print('2个线程对同一个全局变量操作之后的最终结果是%s' %g_num)
    

    运行结果

    33.png

    加大num

    ...
    t1 = threading.Thread(target=work1, args=(1000000,))
    t1.start()
    
    t2 = threading.Thread(target=work2, args=(1000000,))
    t2.start()
    ...
    
    44.jpg

    再次总结

    如果多个线程同时对同一个全局变量操作,会出现资源竞争问题,从而数据结果会不正确。

    相关文章

      网友评论

          本文标题:线程

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