首页 > 编程语言 >Python系列(10)- Python 多线程

Python系列(10)- Python 多线程

时间:2024-08-28 11:04:44浏览次数:5  
标签:24 10 Aug Thread Python 线程 time print 多线程


多线程(Multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的系统包括对称多处理机、多核心处理器、芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作 “线程”(Thread),利用它编程的概念就叫作 “多线程处理”。

多线程是并行化的一种形式,或者是拆分工作以便同时进行处理。线程化的程序将工作拆分到多个软件线程,而不是将大量工作交给单个内核。这些线程由不同的 CPU 内核并行处理,以节省时间。

多线程运行有如下优点:

    (1) 使用线程可以把占据长时间的程序中的任务放到后台去处理;
    (2) 用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度;
    (3) 程序的运行速度可能加快;
    (4) 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。

线程可以分为:

    (1) 内核线程:由操作系统内核创建和撤销。
    (2) 用户线程:不需要内核支持而在用户程序中实现的线程。

并行和并发的区别:

  并行(Parallelism):是同一时刻,每个线程都在执行。
  并发(Concurrency):是同一时刻,只有一个线程执行,然后交替执行。Python 多线程是并发的。

Python 中与多线程相关的 2 个模块:

    (1) _thread
    (2) threading (推荐使用)

    注:thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 Python3 中不能再使用 "thread" 模块。为了兼容性,Python3 将 thread 重命名为 "_thread"。

 

1. _thread 模块

    _thread 模块提供低级别的接口,用于支持轻量级的多线程开发,也提供了简单的锁机制(也称为 互斥锁 或 二进制信号)。

    调用 _thread 模块中的 start_new_thread( )函数来产生新线程。语法格式如下:

        _thread.start_new_thread(function, args[, kwargs])

    参数说明:

        function - 线程函数。
        args - 传递给线程函数的参数,他必须是个tuple类型。
        kwargs - 可选参数。

    示例:

        #!/usr/bin/python3
        # -*- coding: UTF-8 -*-

        import _thread
        import time

        def print_time( name, delay):
            count = 0
            while count < 3:
                time.sleep(delay)
                count += 1
                print ("%s: %s" % ( name, time.ctime(time.time()) ))
            print("%s 结束" % name)

        _thread.start_new_thread(print_time, ("Thread-1", 2))
        _thread.start_new_thread(print_time, ("Thread-2", 4))

        print("同时按下 Ctrl + C 键退出")
        while True:
            pass

    输出结果如下:

        同时按下 Ctrl + C 键退出
        Thread-1: Sat Aug 24 13:02:54 2024
        Thread-2: Sat Aug 24 13:02:56 2024
        Thread-1: Sat Aug 24 13:02:56 2024
        Thread-1: Sat Aug 24 13:02:58 2024
        Thread-1 结束
        Thread-2: Sat Aug 24 13:03:00 2024
        Thread-2: Sat Aug 24 13:03:04 2024
        Thread-2 结束
        Traceback (most recent call last):
        File "d:/pythonDemo/thread.py", line 23, in <module>
            pass
        KeyboardInterrupt

        注:两个线程结束后,主程序仍然处于 while True 循环阻塞,需要在控制台按下 Ctrl + C 键才能退出。
            
    显然,while True 循环阻塞不是很好的线程同步方式,我们可以使用 _thread.allocate_lock() 来实现线程同步,修改代码如下:

        #!/usr/bin/python3
        # -*- coding: UTF-8 -*-

        import _thread
        import time

        def print_time( name, delay, lock):
            count = 0
            while count < 3:
                time.sleep(delay)
                count += 1
                print ("%s: %s" % ( name, time.ctime(time.time()) ))
            print("%s 结束" % name)
            lock.release()

        lock1 = _thread.allocate_lock()
        lock1.acquire()
        _thread.start_new_thread(print_time, ("Thread-1", 2, lock1))

        lock2 = _thread.allocate_lock()
        lock2.acquire()
        _thread.start_new_thread(print_time, ("Thread-2", 4, lock2))

        while lock1.locked() or lock2.locked():
            pass


    输出结果如下:

        Thread-1: Sat Aug 24 17:24:14 2024
        Thread-2: Sat Aug 24 17:24:16 2024
        Thread-1: Sat Aug 24 17:24:16 2024
        Thread-1: Sat Aug 24 17:24:18 2024
        Thread-1 结束
        Thread-2: Sat Aug 24 17:24:20 2024
        Thread-2: Sat Aug 24 17:24:24 2024
        Thread-2 结束

      注:两个线程结束后,主程序自动退出。


2. threading 模块

    threading 模块除了包含 _thread 模块中的所有方法外,还提供如下方法:

        (1) threading.Thread(target, args=(), kwargs={}, daemon=None): 创建 Thread 类的实例;
        (2) threading.current_thread(): 返回当前的线程变量;
        (3) threading.enumerate(): 返回一个包含正在运行的线程的列表。正在运行指线程启动后、结束前,不包括启动前和终止后的线程;
        (4) threading.active_count(): 返回正在运行的线程数量,与 len(threading.enumerate()) 有相同的结果;

    threading.Thread() 返回的 Thread 实例包含如下方法与属性:

        (1) start():开始线程活动(只能执行一次);
        (2) run():线程的方法,在线程被 cpu 调度后,就会自动执行这个方法,可以重写;
        (3) join():等待,直到线程结束;
        (4) setName():给线程设置名字;
        (5) getName():获取线程名字;
        (6) is_alive():返回线程是否存活( True 或者 False);
        (7) daemon: 守护线程,默认是 False;

    1) 使用 threading.Thread() 创建线程

        示例:

            #!/usr/bin/python3
            # -*- coding: UTF-8 -*-

            import threading
            import time

            def print_time( name, delay):
                count = 0
                while count < 3:
                    time.sleep(delay)
                    count += 1
                    print ("%s: %s" % ( name, time.ctime(time.time()) ))
                print("%s finish" % name)

            thread1 = threading.Thread(target=print_time, args=("Thread-1", 2))
            thread2 = threading.Thread(target=print_time, args=("Thread-2", 4))

            thread1.start()
            thread2.start()

            print('主程序结束')

        输出结果如下:

            主程序结束
            Thread-1: Sat Aug 24 14:05:32 2024
            Thread-2: Sat Aug 24 14:05:34 2024
            Thread-1: Sat Aug 24 14:05:34 2024
            Thread-1: Sat Aug 24 14:05:36 2024
            Thread-1 结束
            Thread-2: Sat Aug 24 14:05:38 2024
            Thread-2: Sat Aug 24 14:05:42 2024
            Thread-2 结束


    2) 继承 threading.Thread 创建子类线程

        示例:

            #!/usr/bin/python3
            # -*- coding: UTF-8 -*-

            import threading
            import time

            class TestThread(threading.Thread):
                def __init__(self, name, delay):
                    super().__init__()
                    self.name = name
                    self.delay = delay

                def run(self):
                    print_time(self.name, self.delay)
                    print("%s 结束" % self.name)

            def print_time( name, delay):
                count = 0
                while count < 3:
                    time.sleep(delay)
                    count += 1
                    print ("%s: %s" % ( name, time.ctime(time.time()) ))
                
            start = time.time()

            thread1 = TestThread("Thread-1", 2)
            thread2 = TestThread("Thread-2", 4)

            thread1.start()
            thread2.start()

            # 等待线程都结束
            thread1.join()
            thread2.join()

            end = time.time()

            print('主程序结束', int(end-start), '秒')

        输出结果如下:

            Thread-1: Sat Aug 24 17:40:40 2024
            Thread-2: Sat Aug 24 17:40:42 2024
            Thread-1: Sat Aug 24 17:40:42 2024
            Thread-1: Sat Aug 24 17:40:44 2024
            Thread-1 结束
            Thread-2: Sat Aug 24 17:40:46 2024
            Thread-2: Sat Aug 24 17:40:50 2024
            Thread-2 结束
            主程序结束 12 秒

        注:在主程序里调用线程的 join() 方法,实现多个线程的同步。

 

3. 线程锁

    有些场景我们不希望多个线程同时读写同一个数据,为了保证数据的正确性,需要管理线程对数据读写。

    可以使用 threading 模块的 Lock 或 Rlock 线程锁,两者都有 acquire 方法和 release 方法,可以将数据的读写操作放在 acquire 和 release 方法之间。

    示例:

        #!/usr/bin/python3
        # -*- coding: UTF-8 -*-

        import threading
        import time

        class TestThread(threading.Thread):
            def __init__(self, name, delay):
                super().__init__()
                self.name = name
                self.delay = delay

            def run(self):
                threadLock.acquire()
                print_time(self.name, self.delay)
                print("%s 结束" % self.name)
                threadLock.release()

        def print_time( name, delay):
            count = 0
            while count < 3:
                time.sleep(delay)
                count += 1
                print ("%s: %s" % ( name, time.ctime(time.time()) ))

        threadLock = threading.Lock()

        start = time.time()

        thread1 = TestThread("Thread-1", 2)
        thread2 = TestThread("Thread-2", 4)

        thread1.start()
        thread2.start()

        thread1.join()
        thread2.join()

        end = time.time()

        print('主程序结束', int(end-start), '秒')

    输出结果如下:

        Thread-1: Sat Aug 24 18:42:01 2024
        Thread-1: Sat Aug 24 18:42:03 2024
        Thread-1: Sat Aug 24 18:42:05 2024
        Thread-1 结束
        Thread-2: Sat Aug 24 18:42:09 2024
        Thread-2: Sat Aug 24 18:42:13 2024
        Thread-2: Sat Aug 24 18:42:17 2024
        Thread-2 结束
        主程序结束 18 秒            

    注:使用了线程锁之后,Thread-2 等 Thread-1 结束后才开始操作数据,程序总用时比没使用线程锁时增加了 6 秒。


4. 守护线程

    使用 threading 模块创建的线程,有两种类型:守护线程(Daemon thread)和非守护线程(Non-daemon thread)。默认情况下,当主线程结束时,所有非守护线程会继续运行。而守护线程会在主线程结束时自动退出。

    示例:

        #!/usr/bin/python3
        # -*- coding: UTF-8 -*-

        import threading
        import time

        def print_time( name, delay):
            while True:
                time.sleep(delay)
                print ("%s: %s" % ( name, time.ctime(time.time()) ))

        thread1 = threading.Thread(target=print_time, args=("Thread-1", 2))
        thread2 = threading.Thread(target=print_time, args=("Thread-2", 4))

        print('thread1.daemon:', thread1.daemon)
        print('thread2.daemon:', thread2.daemon)

        thread1.start()
        thread2.start()

        print('主程序结束')


    输出结果如下:

        thread1.daemon: False
        thread2.daemon: False
        主程序结束
        Thread-1: Sat Aug 24 20:58:18 2024
        Thread-2: Sat Aug 24 20:58:20 2024
        Thread-1: Sat Aug 24 20:58:20 2024
        Thread-1: Sat Aug 24 20:58:22 2024
        Thread-2: Sat Aug 24 20:58:24 2024
        Thread-1: Sat Aug 24 20:58:24 2024
        Thread-1: Sat Aug 24 20:58:26 2024
        Thread-2: Sat Aug 24 20:58:28 2024
        ...

        注:当主线程结束时,非守护线程还会继续运行。
    
    我们把 thread1、thread2 都改成守护线程,修改代码如下:

         #!/usr/bin/python3
        # -*- coding: UTF-8 -*-

        import threading
        import time

        def print_time( name, delay):
            while True:
                time.sleep(delay)
                print ("%s: %s" % ( name, time.ctime(time.time()) ))

        thread1 = threading.Thread(target=print_time, args=("Thread-1", 2))
        thread2 = threading.Thread(target=print_time, args=("Thread-2", 4))

        thread1.daemon = True
        thread2.daemon = True

        print('thread1.daemon:', thread1.daemon)
        print('thread2.daemon:', thread2.daemon)

        thread1.start()
        thread2.start()

        print('主程序结束')

    输出结果如下:       

        thread1.daemon: True
        thread2.daemon: True
        主程序结束

        注:当主线程结束时,守护线程自动退出了。

 

5. 线程优先级队列(Queue)

    Python 的 Queue 模块中提供了同步的、线程安全的队列类,包括 FIFO(先入先出)队列 Queue,LIFO(后入先出)队列 LifoQueue,和优先级队列 PriorityQueue。

    这些队列都实现了锁原语,能够在多线程中直接使用,可以使用队列来实现线程间的同步。

    Queue 模块中的常用方法:

        (1) qsize():返回队列的大小;
        (2) empty():如果队列为空,返回 True,反之 False;
        (3) full():如果队列满了,返回 True,反之 False;
        (4) get([block[, timeout]]):获取队列,timeout 等待时间;
        (5) get_nowait():相当于 get(False);
        (6) put(item):写入队列;
        (7) put_nowait(item):相当于 put(item, False);
        (8) task_done():在完成一项工作之后,Queue.task_done() 函数向任务已经完成的队列发送一个信号;
        (9) join():实际上意味着等到队列为空,再执行别的操作;

    示例:

        #!/usr/bin/python3
        # -*- coding: UTF-8 -*-

        import queue
        import threading
        import time

        class TestThread(threading.Thread):
            def __init__(self, name, delay, q):
                super().__init__()
                self.name = name
                self.delay = delay
                self.q = q

            def run(self):
                print_time(self.name, self.delay, self.q)
                print("%s 结束" % self.name)

        def print_time( name, delay, q):
            while q.qsize() > 0:
                i = q.get()
                time.sleep(delay)
                print ("%s: %s" % ( name, time.ctime(time.time()) ))
                q.task_done()   # 任务处理完成,通知队列

        start = time.time()

        # 创建 Queue 任务队列
        q = queue.Queue()
        for i in range(6):
            q.put(i)

        print('Queue 队列: ', q.qsize(), '任务')

        thread1 = TestThread("Thread-1", 2, q)
        thread2 = TestThread("Thread-2", 4, q)

        thread1.start()
        thread2.start()

        q.join()

        end = time.time()

        print('主程序结束', int(end-start), '秒')


    输出结果如下:

        Queue 队列:  6 任务
        Thread-1: Sat Aug 24 21:21:49 2024
        Thread-1: Sat Aug 24 21:21:51 2024
        Thread-2: Sat Aug 24 21:21:51 2024
        Thread-1: Sat Aug 24 21:21:53 2024
        Thread-1: Sat Aug 24 21:21:55 2024
        Thread-2: Sat Aug 24 21:21:55 2024
        Thread-1 结束
        Thread-2 结束
        主程序结束 8 秒

 

标签:24,10,Aug,Thread,Python,线程,time,print,多线程
From: https://www.cnblogs.com/tkuang/p/18384196

相关文章

  • 代码随想录算法day24 | 贪心算法part02 | 122.买卖股票的最佳时机II,55. 跳跃游戏,45.跳
    122.买卖股票的最佳时机II本题解法很巧妙,本题大家可以先自己思考一下然后再看题解,会有惊喜!力扣题目链接(opensnewwindow)给定一个数组,它的第 i个元素是一支给定股票第i天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次......
  • Python酷库之旅-第三方库Pandas(104)
    目录一、用法精讲451、pandas.DataFrame.pow方法451-1、语法451-2、参数451-3、功能451-4、返回值451-5、说明451-6、用法451-6-1、数据准备451-6-2、代码示例451-6-3、结果输出452、pandas.DataFrame.dot方法452-1、语法452-2、参数452-3、功能452-4、返回值......
  • 洛谷P10931 闇の連鎖
    洛谷P10931闇の連鎖题意给定一棵\(n\)个点的树,有\(m\)条附加边。第一次删除一条树边,第二次删除一条附加边。求有多少种方案使原来的树不联通。思路考虑求出\(f_i\)表示\(i\)的子树中有多少条附加边连向\(i\)的子树外。若\(f_i=0\),则把\(i\)与\(i\)的父亲之间......
  • Python画笔案例-017 绘制画H图
    1、绘制画H图通过python的turtle库绘制一个画H图的图案,如下图:2、实现代码 绘制一个画H图图案,以下实现的代码直接按移动,左转,右转的方式实现,大家可以尝试把本程序改成递归图,要点为在下面的dot命令修改。相信你一定能完成。:"""画H图.py"""importturtle......
  • 【Hot100】LeetCode—39. 组合总和
    目录1-思路2-实现⭐39.组合总和——题解思路3-ACM实现题目连接:39.组合总和1-思路注意如果借助startIndex实现,理解startIndex的含义在本题目中,由于同一个元素可以重复选取,因此startIndex在传递的过程中,不需要进行+1操作,传递的值为i2-实现⭐39......
  • 【Hot100】LeetCode—17. 电话号码的字母组合
    目录1-思路String数组(哈希表)+回溯2-实现⭐17.电话号码的字母组合——题解思路3-ACM实现题目连接:17.电话号码的字母组合1-思路String数组(哈希表)+回溯思路通过String数组实现哈希表,存储0-9的哈希表映射回溯三部曲①参数及返回值numToStr:Stri......
  • QT/C++中的GDAL多线程应用(读取):发生的问题以及解决方案
    1.引言在使用GDAL库对TIF文件进行切割和创建瓦片金字塔时,为了提高创建效率,不得不考虑使用多线程处理。然而,在实际实现过程中,我遇到了许多问题。通过不断的尝试和优化,最终找到了有效的解决方案。本文将详细记录这一过程中的问题和解决方法。2.初始多线程尝试与问题2.1......
  • yum依赖python2环境-"No module named urlgrabber"
    1.python3安装perl环境以及IPC/cmd.pm模块,由于环境中安装了pyhon2和python3导致模块引入冲突。makepython3时一直报错没有Module_tktinter,重新安装tk后python3还是import失败 2.检查发现python2可以引入,并且再进行安装模块时,使用的是python,而系统python指向python2 3.修改......
  • 简答登陆采集python
    importparamikoimportos创建SSH对象ssh=paramiko.SSHClient()允许连接不在know_hosts文件中的主机ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())连接服务器ssh.connect(hostname='10.10.10.10',port=22,username='root',password='root123�......
  • FlexAttention:解决二次复杂度问题,将大型视觉语言模型的输入提升至1008 | ECCV 2024
    \({\ttFlexAttention}\)是一种旨在增强大型视觉语言模型的方法,通过利用动态高分辨率特征选择和分层自注意机制,使其能够有效地处理并从高分辨率图像输入中获得优势,\({\ttFlexAttention}\)在性能和效率方面超越了现有的高分辨率方法。来源:晓飞的算法工程笔记公众号论文:F......