Python多线程从入门到精通

Python多线程与单线程的区别


            🔥🔥点个赞呗,求求了🙏🙏🙏


单线程

  • 定义:单线程意味着程序在任何时刻只能执行一个任务。程序的执行路径是线性的,一次只能执行一段代码。‌
  • 优点:程序结构简单,易于理解和调试。‌
  • 缺点:执行效率可能较低,无法同时处理多个任务。对于需要快速响应或同时执行多项操作的应用来说,可能不够高效。‌

多线程

  • 定义:多线程允许程序同时执行多个任务。这是通过创建独立的执行路径(线程)实现的,每个线程可以独立地执行任务。‌
  • 优点:能够同时执行多个任务,提高程序的执行效率和响应速度,特别是在多核处理器上,可以显著提高应用程序的性能。‌
  • 缺点:程序设计更为复杂,需要处理线程间的同步和通信,可能出现死锁等多线程相关的问题。

  • 一个线程完整的生命周期包括新建——就绪——运行——阻塞——死亡。
  • 新建:即新创建一个线程对象
  • 就绪:调用start方法后,线程对象等待运行,什么时候开始运行取决于调度
  • 运行:线程处于运行状态
  • 阻塞:处于运行状态的线程被堵塞,通俗理解就是被卡住了,可能的原因包括但不限于程序自身调用sleep方法阻塞线程运行,或调用了一个阻塞式I/O方法,被阻塞的进程会等待何时解除阻塞重新运行
  • 死亡:线程执行完毕或异常退出,线程对象被销毁并释放内存

什么是多线程?

  • 线程(thread)是操作系统中能够进行运算的最小单位,包含于进程之中,一个进程可以有多个线程,这意味着一个进程中可以并发多个线程,即为多线程。
  • 对于一个python程序,如果需要同时大量处理多个任务,有使用多进程和多线程两种方法。在python中,实现多线程主要通过threading模块,而多进程主要通过multiprocessing模块。
  • 这两个模块的主要区别是:threading模块基于线程,而multiprocessing模块基于进程。threading模块使用共享内存来实现多线程,所有线程都共享一样的变量(这点在后续的实例中可以感受到);而multiprocessing基于子进程,每个子进程之间都有独立的变量和数据结构。两者的区别意味着threading更使用于I/O密集型任务(例如需要进行多表格读取操作),multiprocessing模块更适用于包含较多计算的CPU密集型任务(矩阵运算,图片处理类任务)。
  • 需要注意的是,由于python中的GIL锁的存在,Python解释器只允许一个Python进程使用,这意味着对于一个解释器只允许一个进程在运行,这也是为什么threading模块无法适用于CPU密集型这类需要大量CPU资源的任务,因为一个进程的CPU资源有限,无论开启多少个线程,总的资源就只有那些,总耗时不会有太大变化。而multiprocessing模块则可以开多个进程,能够更快速的处理CPU密集型任务。
  • 关于GIL锁和Multiprocessing模块的部分就不继续深入介绍了,本次主要介绍如何使用threading模块实现多线程的相关内容。

1. 线程基础创建

(难度: 💦)Python中threading的多线程创建方法代码示例

import threading

import time

 

# 定义线程执行的函数

def thread_function(name):

    print(f"Thread {name} is running...")

    time.sleep(2)

    print(f"Thread {name} completed")

 

# 创建线程

thread1 = threading.Thread(target=thread_function, args=("Thread 1",))

thread2 = threading.Thread(target=thread_function, args=("Thread 2",))

 

# 启动线程

thread1.start()

thread2.start()

 

# 等待线程完成

thread1.join()

thread2.join()

 

print("All threads completed")

原理分析:

  •     在上述示例中,定义了一个名为  thread_function  的函数,作为线程执行的任务。然后创建了两个线程  thread1  和  thread2  ,并通过  start  方法启动线程。使用  join  方法等待线程执行完毕,确保主线程在子线程完成后再继续执行

(难度: ❄)Python中进程池创建多线程的创建方法代码示例

import multiprocessing

import time

 

def worker_task(task_id):

    print(f"Task {task_id} is being processed by process {multiprocessing.current_process().name}")

    time.sleep(2)

    print(f"Task {task_id} completed")

 

if __name__ == "__main__":

    # 创建进程池,最大进程数为 4

    pool = multiprocessing.Pool(processes=4)

 

    # 提交任务到进程池

    for task_id in range(8):

        pool.apply_async(worker_task, args=(task_id,))

 

    # 关闭进程池,不再接受新任务

    pool.close()

 

    # 等待所有任务完成

    pool.join()

 

    print("All tasks completed")

原理分析:

  • 在上述代码中,定义了一个  worker_task  函数作为要执行的任务。在  if __name__ == "__main__":  中创建了一个最大进程数为 4 的进程池,然后通过循环向进程池提交 8 个任务。使用  apply_async  方法以异步方式执行任务。最后关闭进程池并等待所有任务完成。
  • 进程池可以有效地管理和复用进程资源,适用于需要并行处理多个任务的场景。

(难度:🔥)Python中queue与threading多线程的创建方法

import threading

import queue

import time

 

# 工作函数,处理数据

def worker(worker_id, data_queue, result_queue):

    while True:

        if not data_queue.empty():

            data = data_queue.get()

            print(f"Worker {worker_id} is processing data: {data}")

            result = data * 2

            result_queue.put(result)

            time.sleep(1)

        else:

            break

 

if __name__ == "__main__":

    data_queue = queue.Queue()

    result_queue = queue.Queue()

 

    # 向队列中添加数据

    for i in range(10):

        data_queue.put(i)

 

    threads = []

    # 创建并启动 3 个线程

    for i in range(3):

        thread = threading.Thread(target=worker, args=(i, data_queue, result_queue))

        thread.start()

        threads.append(thread)

 

    # 等待所有线程完成

    for thread in threads:

        thread.join()

 

    # 取出结果

    while not result_queue.empty():

        print(f"Result: {result_queue.get()}")


2. 线程高级操作

  • 注释:

Newthread: 创建的线程对象
function: 要执行的函数
argument1,argument2: 传递给线程函数的参数,为tuple类型

  • 2.1使用join阻塞线程

 
from threading import Thread
import time
from time import sleep
 
 
# 自定义的函数,可以替换成其他任何函数
def task(threadName, number, letter):
    print(f"【线程开始】{threadName}")
    m = 0
    while m < number:
        sleep(1)
        m += 1
        current_time = time.strftime('%H:%M:%S', time.localtime())
        print(f"[{current_time}] {threadName} 输出 {letter}")
    print(f"【线程结束】{threadName}")
 
 
thread1 = Thread(target=task, args=("thread_1", 6, "a"))  # 线程1:假设任务为打印6个a
thread2 = Thread(target=task, args=("thread_2", 4, "b"))  # 线程2:假设任务为打印4个b
thread3 = Thread(target=task, args=("thread_3", 2, "c"))  # 线程3:假设任务为打印2个c
 
thread1.start()  # 线程1启动
thread2.start()  # 任务2启动
thread2.join()   # 等待线程2
thread3.start()  # 线程2完成任务后线程3才启动
thread1.join()   # 等待线程1完成线程
thread3.join()   # 等待线程3完成线程

 

    2.1输出结果

【线程开始】thread_1
【线程开始】thread_2

thread_2 输出 b
thread_1 输出 a
thread_2 输出 b
 thread_1 输出 a
thread_2 输出 b
thread_1 输出 a
thread_2 输出 b
thread_2
thread_1 输出 a
thread_3
thread_3 输出 c
thread_1 输出 a
thread_1 输出 a
thread_3 输出 c
【线程结束】thread_3
【线程结束】thread_1

 

 

  • 2.2使用threading.Thread重写父类构造函数


import threading
import time
from time import sleep
 
 
# myThread继承父类,并进行重写
class myThread(threading.Thread):
    # 重写父类的构造函数
    def __init__(self, number, letter):
        threading.Thread.__init__(self)
        self.number = number  # 添加number变量
        self.letter = letter  # 添加letter变量
 
    # 重写父类中的run函数
    def run(self):
        print(f"【线程开始】{self.name}")
        task1(self.name, self.number, self.letter)
        print("【线程结束】", self.name)
 
    # 重写父类析构函数
    def __del__(self):
        print("【线程销毁释放内存】", self.name)
 
 
# 自定义的函数,此处可以替换成任何其他想要多线程执行的任务
def task1(threadName, number, letter):
    m = 0
    while m < number:
        sleep(1)
        m += 1
        current_time = time.strftime('%H:%M:%S', time.localtime())
        print(f"[{current_time}] {threadName} 输出 {letter}")
 
# def task2...
# def task3...
 
 
thread1 = myThread(4, "a")  # 创建线程thread1
thread2 = myThread(2, "b")  # 创建线程thread2
 
thread1.start()  # 启动线程1
thread2.start()  # 启动线程2
 
thread1.join()  # 等待线程1
thread2.join()  # 等待线程2

 

    2.2输出结果:

【线程开始】Thread-1
【线程开始】Thread-2

 Thread-1 输出 a
 Thread-2 输出 b
 Thread-1 输出 a

 Thread-2 输出 b
【线程结束】 Thread-2
 Thread-1 输出 a
 Thread-1 输出 a
【线程结束】 Thread-1
【线程销毁释放内存】 Thread-1
【线程销毁释放内存】 Thread-2

 

  • 2.3线程守护

import threading

import time

from time import sleep

 

 

# myThread继承父类,并进行重写

class myThread(threading.Thread):

    # 重写父类的构造函数

    def __init__(self, number, letter):

        threading.Thread.__init__(self)

        self.number = number # 添加number变量

        self.letter = letter # 添加letter变量

        self.daemon = True # 默认前台线程

 

    # 重写父类中的run函数

    def run(self):

        print(f"【线程开始】{self.name}")

        task1(self.name, self.number, self.letter)

        print("【线程结束】", self.name)

 

    # 重写父类析构函数

    def __del__(self):

        print("【线程销毁释放内存】", self.name)

 

 

# 自定义的函数,此处可以替换成任何其他想要多线程执行的任务

def task1(threadName, number, letter):

    m = 0

    while m < number:

        sleep(1)

        m += 1

        current_time = time.strftime('%H:%M:%S', time.localtime())

        print(f"[{current_time}] {threadName} 输出 {letter}")

 

# def task2...

# def task3...

 

 

thread1 = myThread(4, "a") # 创建线程thread1:假设任务耗时2s

thread2 = myThread(2, "b") # 创建线程thread2:假设任务耗时4s

 

thread1.start() # 启动线程1

thread2.start() # 启动线程2

 

time.sleep(3) # 主程序等待3s再继续执行

    2.3输出结果

【线程开始】Thread-1

【线程开始】Thread-2

Thread-1 输出 a

Thread-2 输出 b

Thread-1 输出 a

Thread-2 输出 b

【线程结束】 Thread-2

  •  2.4线程同步

为了将各个线程同步,我们引入线程锁的概念。当某个线程访问数据时,先对其加锁,其他线程若再想访问这个数据就会被阻塞,直到前一个线程解锁释放。在threading模块中,加锁和释放锁主要使用Lock类,使用其中的acquire()和release()方法:

Lock = threading.Lock() # 在threading中获得锁类

Lock.acquire() # 设置锁

Lock.release() # 释放锁

import threading

import time

  

# 子类myThread继承父类threading.Thread,并进行重写

class myThread(threading.Thread):

    # 重写父类构造函数

    def __init__(self, number):

        threading.Thread.__init__(self)

        self.number = number

 

    # 重写父类run函数,在调用start()时自动调用run函数

    def run(self):

        print(f"【线程开始】{self.name}")

        Lock.acquire() # 设置线程锁

        edit_list(self.name, self.number)

        Lock.release() # 释放线程锁

 

    # 重写父类析构函数

    def __del__(self):

        print("【线程销毁】", self.name)

 

 

# 自定义的任务函数

def edit_list(threadName, number):

    while number > 0:

        time.sleep(1)

        data_list[number-1] += 1

        current_time = time.strftime('%H:%M:%S', time.localtime())

        print(f"[{current_time}] {threadName} 修改datalist为{data_list}")

        number -= 1

    print(f"【线程{threadName}完成工作】")

 

 

data_list = [0, 0, 0, 0]

Lock = threading.Lock()

 

# 创建3个子线程

thread1 = myThread(1)

thread2 = myThread(2)

thread3 = myThread(3)

 

# 启动3个子线程

thread1.start()

thread2.start()

thread3.start()

 

# 主进程等待所有线程完成

thread1.join()

thread2.join()

thread3.join()

 

print("【主进程结束】")

    2.4输出结果

【线程开始】Thread-1

【线程开始】Thread-2

【线程开始】Thread-3

Thread-1 修改datalist为[1, 0, 0, 0]

【线程Thread-1完成工作】

Thread-2 修改datalist为[1, 1, 0, 0]

Thread-2 修改datalist为[2, 1, 0, 0]

【线程Thread-2完成工作】

Thread-3 修改datalist为[2, 1, 1, 0]

Thread-3 修改datalist为[2, 2, 1, 0]

Thread-3 修改datalist为[3, 2, 1, 0]

【线程Thread-3完成工作】

【主进程结束】

【线程销毁】 Thread-1

【线程销毁】 Thread-2

【线程销毁】 Thread-3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值