zoukankan      html  css  js  c++  java
  • python多任务-线程

    多任务的概念

    什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。

    现在,多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务。由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢?

    答案就是操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。

    真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。

    注意:

    • 并发:指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
    • 并行:指的是任务数小于等于cpu核数,即任务真的是一起执行的

    线程基础

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

    单线程执行

    import time
    
    
    def test():
            print("test...")
            time.sleep(1)
    
    
    if __name__ == '__main__':
    
            for i in range(5):
                    test()
    
    

    执行效果:程序在控制台每隔一秒打印test...

    多线程执行

    import time
    import threading
    
    def test():
            print("test...")
            time.sleep(1)
    
    
    if __name__ == '__main__':
    
            for i in range(5):
                    t = threading.Thread(target=test)
                    t.start() # 启动线程
        
    

    执行效果:程序在控制台一下子输出五行test...,等待1秒左右结束

    说明:

    1. 可以明显看出使用了多线程并发的操作,花费时间要短很多
    2. 当调用start()时,才会真正的创建线程,并且开始执行

    主线程会等待所有子线程结束后才结束

    import time
    import threading
    
    
    def playPhone():
    
            print('玩手机...')
            time.sleep(1)
    
    
    def eat():
    
            print("吃东西...")
            time.sleep(1)
    
    
    if __name__ == '__main__':
    
            print("--开始--")
    
            t1 = threading.Thread(target=playPhone)
            t1.start()
    
            t2 = threading.Thread(target=eat)
            t2.start()
    
            print('--执行结束')
    
    

    执行效果:主线程阻塞1秒左右后程序结束,说明主线程在等待其他线程执行完毕。

    查看线程数量

              print('玩手机...')
                    time.sleep(1)
    
    
    def eat():
    
            for i in range(10):
                    print("吃东西...")
                    time.sleep(1)
    
    
    if __name__ == '__main__':
    
            print("--开始--")
    
            t1 = threading.Thread(target=playPhone)
            t1.start()
    
            t2 = threading.Thread(target=eat)
            t2.start()
        
            # 查看正在执行的线程数量
            while True:
                    length = len(threading.enumerate())
                    print("当前运行的线程数量:%d" % length)
                    print("这些线程是:%s" % str(threading.enumerate()))
        
        
                    if length == 1:
                            break
        
                    time.sleep(0.5)    
    
            print('--执行结束')
    

    在python中,调用threading.enumerate()能获取当前正在运行的所有线程,返回值是一个list,调用length()函数并传入该list对象就获取到当前运行线程的数量。

    线程-注意点

    线程执行代码的封装

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

    示例如下:

    import time
    import threading
    
    
    class MyThread(threading.Thread):
    
            def run(self):
                    for i in range(5):
                            time.sleep(1)
                            print("我是%s@%d" % (self.name, i)) 
    
    
    if __name__ == "__main__":
            mt = MyThread()
            mt.start()
    

    运行结果如下:

    我是Thread-1@0
    我是Thread-1@1
    我是Thread-1@2
    我是Thread-1@3
    我是Thread-1@4
    

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

    线程的执行顺序

    mport time
    import threading
    
    
    class MyThread(threading.Thread):
    
            def run(self):
                    for i in range(5):
                            time.sleep(1)
                            print("我是%s@%d" % (self.name, i)) 
    
    
    if __name__ == "__main__":
        
            for i in range(5):
                    mt = MyThread()
                    mt.start()
    

    执行结果(运行的结果可能不一样,但是大体是一致的):

    我是Thread-5@0
    我是Thread-2@0
    我是Thread-3@0
    我是Thread-4@0
    我是Thread-1@0
    我是Thread-3@1
    我是Thread-4@1
    我是Thread-2@1
    我是Thread-5@1
    我是Thread-1@1
    我是Thread-3@2
    我是Thread-4@2
    我是Thread-2@2
    我是Thread-5@2
    我是Thread-1@2
    我是Thread-3@3
    我是Thread-4@3
    我是Thread-2@3
    我是Thread-5@3
    我是Thread-1@3
    我是Thread-3@4
    我是Thread-4@4
    我是Thread-5@4
    我是Thread-2@4
    我是Thread-1@4
    

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

    总结

    1. 每个线程默认有一个名字,尽管上面的例子中没有指定线程对象的name,但是python会自动为线程指定一个名字。
    2. 当线程的run()方法结束时该线程完成。
    3. 无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。
  • 相关阅读:
    7月15日考试 题解(链表+状压DP+思维题)
    暑假集训日记
    C# .NET 使用 NPOI 生成 .xlsx 格式 Excel
    JavaSE 基础 第42节 局部内部类
    JavaSE 基础 第41节 匿名内部类
    JavaSE 基础 第40节 内部类概述
    JavaSE 基础 第39节 接口的应用
    JavaSE 基础 第38节 接口的实现
    JavaSE 基础 第37节 接口概述
    JavaSE 基础 第36节 抽象类概述与使用
  • 原文地址:https://www.cnblogs.com/zhangfengxian/p/python-thread.html
Copyright © 2011-2022 走看看