• 「Python入门」Python多线程


    活动地址:CSDN21天学习挑战赛


    前言

    线程 也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
    多线程 线程在程序中是独立的、并发的执行流。与分隔的进程相比,进程中线程之间的隔离程度要小,它们共享内存、文件句柄和其他进程应有的状态。

    • 进程之间不能共享内存,但线程之间共享内存非常容易。
    • 操作系统在创建进程时,需要为该进程重新分配系统资源,但创建线程的代价则小得多。因此使用多线程来实现多任务并发执行比使用多进程的效率高。
    • python语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了python的多线程编程。

    在这里插入图片描述


    一、线程与进程的区别

    • 线程共享内存,进程独立内存
    • 线程启动速度块,进程启动速度慢,运行时速度没有可比性
    • 同一个进程的线程间可以直接交流,两个进程想通信,必须通过一个中间代理来实现
    • 创建新线程很简单,创建新进程需要对其父进程进行一次克隆
    • 一个线程可以控制和操作同一线程里的其他线程,但是进程只能操作子进程

    二、多线程的使用方式

    2.1 直接使用

    # 文件名 python1.py
    # -*- coding:utf-8 -*-
    # 线程直接使用
    import threading
    import time
    
    
    # 需要多线程运行的函数
    def fun(args):
        print("我是线程%s" % args)
        time.sleep(2)
        print("线程%s运行结束" % args)
    
    
    # 创建线程
    t1 = threading.Thread(target=fun, args=(1,))
    t2 = threading.Thread(target=fun, args=(2,))
    start_time = time.time()
    t1.start()
    t2.start()
    end_time = time.time()
    print("两个线程一共的运行时间为:", end_time-start_time)
    print("主线程结束")
    
    """
    执行 python python1.py
    运行结果:
    我是线程1
    我是线程2两个线程一共的运行时间为: 0.0019996166229248047
    主线程结束
    
    线程1运行结束
    线程2运行结束
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    在这里插入图片描述

    2.2 继承式调用

    # 文件名 python2.py
    # 继承式调用
    import threading
    import time
    
    
    class MyThreading(threading.Thread):
        def __init__(self, name):
            super(MyThreading, self).__init__()
            self.name = name
    
        # 线程要运行的代码
        def run(self):
            print("我是线程%s" % self.name)
            time.sleep(2)
            print("线程%s运行结束" % self.name)
    
    
    t1 = MyThreading(1)
    t2 = MyThreading(2)
    start_time = time.time()
    t1.start()
    t2.start()
    end_time = time.time()
    print("两个线程一共的运行时间为:", end_time-start_time)
    print("主线程结束")
    
    """
    执行 python python2.py
    运行结果:
    我是线程1
    我是线程2
    两个线程一共的运行时间为: 0.0010724067687988281
    主线程结束
    线程2运行结束
    线程1运行结束
    """
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    在这里插入图片描述

    三、 守护线程

    • 在Python多线程编程中,join方法的作用式线程同步。
    • 守护线程,是为守护别人而存在的,当设置为守护线程后,被守护的主线程不存在后,守护线程也自然不存在。

    Python多线程默认情况(设置线程setDaemon(False)),主线程执行完自己的任务后,就退出了,此时子线程会继续执行自己的任务,直到子线程任务结束
    代码演示:threading中的两个创建多线成的例子都是。

    # 守护线程
    import threading
    import time
    
    
    class MyThreading(threading.Thread):
        def __init__(self, name):
            super(MyThreading, self).__init__()
            self.name = name
    
        # 线程要运行的代码
        def run(self):
            print("我是线程%s" % self.name)
            time.sleep(2)
            print("线程%s运行结束" % self.name)
    
    
    t1 = MyThreading(1)
    t2 = MyThreading(2)
    start_time = time.time()
    t1.setDaemon(True)
    t1.start()
    t2.setDaemon(True)
    t2.start()
    end_time = time.time()
    print("两个线程一共的运行时间为:", end_time-start_time)
    print("主线程结束")
    """
    执行 python python3.py
    后续执行结果以截图的形式呈现,文件名可自定义为xx.py,执行 python xx.py 指令即可.
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    在这里插入图片描述

    四、 join线程同步

    当不给程序设置守护进程时,主程序将一直等待子程序全部运行完成才结束

    # join:线程同步
    import threading
    import time
    
    
    class MyThreading(threading.Thread):
        def __init__(self, name):
            super(MyThreading, self).__init__()
            self.name = name
    
        # 线程要运行的代码
        def run(self):
            print("我是线程%s" % self.name)
            time.sleep(3)
            print("线程%s运行结束" % self.name)
    
    
    threading_list = []
    start_time = time.time()
    for x in range(50):
        t = MyThreading(x)
        t.start()
        threading_list.append(t)
    
    for x in threading_list:
        x.join()    # 为线程开启同步
    
    end_time = time.time()
    print("50个线程一共的运行时间为:", end_time-start_time)
    print("主线程结束")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    在这里插入图片描述

    五、 线程锁(互斥锁Mutex)

    一个进程下可以启用多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据。

    # 线程锁(互斥锁Mutex)
    # -*- coding:utf8  -*-
    import threading
    import time
    
    num = 100
    threading_list = []
    
    
    def fun():
        global num
        print("get num:", num)
        num += 1
        time.sleep(1)
    
    
    for x in range(200):
        t = threading.Thread(target=fun)
        t.start()
        threading_list.append(t)
    
    for x in threading_list:
        x.join()
    
    print("nun:", num)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述

    六、 RLock(递归锁)

    # RLock(递归锁)
    import threading, time
    def run1():
        lock.acquire()
        print("grab the first part data")
        global num
        num += 1
        lock.release()
        return num
    
    
    def run2():
        lock.acquire()
        print("grab the second part data")
        global num2
        num2 += 1
        lock.release()
        return num2
    
    
    def run3():
        lock.acquire()
        res = run1()
        print('--------between run1 and run2-----')
        res2 = run2()
        lock.release()
        print(res, res2)
    
    
    if __name__ == '__main__':
        num, num2 = 0, 0
        lock = threading.RLock()
        for i in range(3):
            t = threading.Thread(target=run3)
            t.start()
    
    while threading.active_count() != 1:
        print(threading.active_count())
    else:
        print('----all threads done---')
        print(num, num2)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    注:在开发的过程中要注意有些操作默认都是 线程安全的(内部集成了锁的机制),我们在使用的时无需再通过锁再处理

    # RLock(递归锁)
    import threading
    
    data_list = []
    
    lock_object = threading.RLock()
    
    
    def task():
        print("开始")
        for i in range(1000000):
            data_list.append(i)
        print(len(data_list))
    
    
    for i in range(2):
        t = threading.Thread(target=task)
        t.start()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    七、 线程池

    线程不是开的越多越好,开的多了可能会导致系统的性能更低了。

    # 线程池
    import time
    from concurrent.futures import ThreadPoolExecutor  # 并行期货,线程池执行者
    """
    pool = ThreadPoolExecutor(100)
    pool.submit(函数名,参数1,参数2,参数...)
    """
    
    
    def task(video_url, num):
        print("开始执行任务", video_url, num)     # 开始执行任务 www.vitian-99.vip 3
        time.sleep(1)
    
    
    # 创建线程池,最多维护10个线程
    threadpool = ThreadPoolExecutor(10)
    # 生成100网址,并放入列表
    url_list = ["www.vitian-{}.vip".format(i) for i in range(100)]
    for url in url_list:
        """
        在线程池中提交一个任务,线程池如果有空闲线程,则分配一个线程去执行,执行完毕后在将线程交还给线程池,
        如果没有空闲线程,则等待。注意在等待时,与主线程无关,主线程依然在继续执行。
        """
        threadpool.submit(task, url, 3)
    
    print("等待线程池中的任务执行完毕中······")
    threadpool.shutdown(True)   # 等待线程池中的任务执行完毕后,在继续执行
    print("END")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在这里插入图片描述

    # 线程池的回调
    import time
    import random
    from concurrent.futures import ThreadPoolExecutor
    
    
    def task(video_url):
        print("开始执行任务", video_url)
        time.sleep(1)
        return random.randint(0, 10)    # 将结果封装成一个Futuer对象,返回给线程池
    
    
    def done(response):     # response就是futuer对象,也就是task的返回值分装的一个Futuer对象
        print("任务执行完后,回调的函数", response.result())    # 即Futuer.result():取出task的返回值
    
    
    # 创建线程池
    threadpool = ThreadPoolExecutor(10)
    url_list = ["www.xxxx-{}.com".format(i) for i in range(5)]
    for url in url_list:
        futuer = threadpool.submit(task, url)    # futuer是由task返回的一个Future对象,里面有记录task的返回值
        futuer.add_done_callback(done)           # 回调done函数,执行者依然是子线程
    
    # 优点:可以做分工,例如:task专门下载,done专门将下载的数据写入本地文件。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

  • 相关阅读:
    Python Fire:更加灵活的命令行参数
    Java编码
    SpringBoot实现多数据源的两种方式
    某大型国有银行 VMware 替换与轻量信创云底座转型实践 |信创专题
    分布式应用kafka + EFLFK集群部署
    Mysql配置binlog并实现数据库备份恢复
    k8s-----7、Pod健康检查
    距离矢量路由协议RIP(含Cisco模拟器实验命令配置)
    MySql常用指令总结
    推荐系统方法梳理
  • 原文地址:https://blog.csdn.net/qq_38209578/article/details/126117853