Python 进阶(二)多线程初探

二 多线程

2.1 单核CPU到多核CPU

  • 随着计算机技术的发展,毫无疑问现代计算机的处理速度和计算能力也越来越强,然而细心的同学们可能早已注意到,从2005年起,单核的 CPU 性能就没有显著的提升了(见下图),究其原因,是人们发现单纯的提高单核 CPU 的性能无论从潜力上还是功耗上都是不合算的。
  • 随着 Intel 的 NetBurst 架构退出江湖,处理器彻底进入了多核时代,从最初的双核一路飙升到现在的动辄上百核的 CPU,性能的提升不以里计。
  • 同时一系列针对特殊计算的 accelerator 的出现,并行硬件的发展现在正是百花齐放。多年以前要用许多台电脑才能并行处理的“大数据”问题,现在大多都可以用一台多核电脑解决了。

2.2 线程与进程

  • Thread线程是操作系统能够进行运算调动的最小单位,它被包含在进程之中,是进程中的实际运作单位,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
  • 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配基本单位,是操作系统结构的基础。


例如"在桌面上双击打开一个App", 桌面App程序会调用OS的系统调用接口fork,让OS 创建一个进程出来,OS为你准备好进程的结构体对象,将这个App的文件(xxx.exe, 存放编译好的代码指令)加载到进程的代码段,同时OS会为你创建一个线程(main thread), 在代码里面,还可以调用OS的接口,来创建多个线程,这样OS就可以调度这些线程执行了,接下来我们来来看python 的线程

2.3 多线程

2.3.1 线程的创建

方式一:传递可调用对象给threading.Thread构造函数
你可以将一个可调用对象(通常是函数或方法)传递给threading.Thread的构造函数,线程将执行这个可调用对象。


import threading

# 定义一个可调用函数
def my_function():
    # 线程的执行逻辑写在这里
    print("线程开始执行")

# 创建线程对象,传递可调用函数
thread2 = threading.Thread(target=my_function)

# 启动线程
thread2.start()

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

print("线程执行完毕")
  • 案例
# ---encoding:utf-8---
# @Time    : 2023/9/11 21:16
# @Author  : Darwin_Bossen
# @Email   :3139066125@qq.com
# @Site    :  Thread 线程
# @File    : ThreadTest.py

import threading


if __name__ == '__main__':
    # 1. 创建线程
    # target: 线程执行的目标函数
    # args: 以元组的方式给线程传参
    # kwargs: 以字典的方式给线程传参
    # name: 线程的名字
    # daemon: 是否是守护线程
    # thread = threading.Thread(target=func, args=(1, 2, 3), kwargs={"a": 1, "b": 2}, name="线程1", daemon=True)
    # thread = threading.Thread(target=func, args=(1, 2, 3), kwargs={"a": 1, "b": 2}, name="线程1")
    # thread = threading.Thread(target=func, args=(1, 2, 3), kwargs={"a": 1, "b": 2})
    # thread = threading.Thread(target=func, args=(1, 2, 3))
    # thread = threading.Thread(target=func)
    thread = threading.Thread()
    # 2. 启动线程
    thread.start()
    # 3. 等待线程执行完毕
    thread.join()
    # 4. 获取线程名字
    print(thread.name)
    # 5. 获取当前线程
    print(threading.current_thread())
    # 6. 获取所有线程
    print(threading.enumerate())
    # 7. 获取活跃线程数量
    print(threading.active_count())
    # 8. 判断线程是否存活
    print(thread.is_alive())


方式二:继承threading.Thread类:
你可以创建一个自定义的线程类,继承自threading.Thread,并覆盖其run方法来定义线程的执行逻辑。

# ---encoding:utf-8---
# @Time    : 2023/9/11 21:32
# @Author  : Darwin_Bossen
# @Email   :3139066125@qq.com
# @Site    :  继承线程创建
# @File    : ThreadTest01.py

import threading

class MyThread(threading.Thread):
    def run(self):
        # 线程的执行逻辑写在这里
        print("线程开始执行")
        print(threading.current_thread())
        print("线程执行结束")


if __name__ == '__main__':
    # 创建线程对象
    thread1 = MyThread()
    # 启动线程
    thread1.start()
    # 等待线程结束
    thread1.join()
    print("线程执行完毕")
  • 无论哪种方式,一旦线程对象被创建并启动,它会执行run方法或可调用对象中的代码,并在完成后退出。
  • 记得在程序结束前使用join方法等待线程执行完毕,以确保主线程不会在子线程之前退出

2.3.2 线程的状态

  1. 新建(New):线程被创建但尚未开始执行。这是线程对象被创建后的初始状态。
  2. 就绪(Runnable):线程已经准备好执行,但尚未获得CPU执行时间。在多线程环境中,多个线程可以处于就绪状态,等待CPU资源。
  3. 运行(Running):线程正在执行其任务,正在使用CPU资源。
  4. 阻塞(Blocked):线程被阻塞,无法执行。这可能是因为线程在等待某些资源(如锁或输入/输出操作)完成,或者它被显式地挂起。
  5. 终止(Terminated):线程已经完成其任务,或者由于某种原因而被终止。一旦线程终止,它不能再次启动。
# ---encoding:utf-8---
# @Time    : 2023/9/11 21:38
# @Author  : Darwin_Bossen
# @Email   :3139066125@qq.com
# @Site    :  线程状态
# @File    : ThreadStatus.py

import threading
import time

# 定义一个简单的线程函数
def thread_function():
    for i in range(5):
        print(f"线程正在执行:{i}")
        time.sleep(1)

# 创建线程对象
thread = threading.Thread(target=thread_function)

# 启动线程
thread.start()

# 检查线程状态
while True:
    if thread.is_alive():
        print("线程仍然在运行...")
    else:
        print("线程已经结束。")
        break

# 主线程等待子线程完成
thread.join()
print("主线程结束。")


if __name__ == '__main__':
    pass

在这个示例中,我们首先创建了一个名为thread_function的简单线程函数,该函数只是循环打印数字并休眠1秒钟。然后,我们创建了一个线程对象,并使用**start()方法启动线程。在主线程中,我们使用is_alive()方法来检查线程是否仍然在运行,如果线程仍在运行,就会不断打印消息,一旦线程结束,就会退出循环。最后,我们使用join()**方法等待线程完成,然后打印主线程结束的消息。

2.3.3 关键函数

Python线程的状态切换通常是由Python解释器和操作系统的线程调度器自动管理的,但是在编写多线程应用程序时,你可以使用一些关键函数来影响线程的状态切换。以下是一些关键的线程管理函数:

  1. threading.Thread(target, args):用于创建一个新的线程对象。target参数指定线程要执行的函数,args参数是传递给线程函数的参数。
  2. start():启动线程。一旦线程启动,它将进入就绪状态并开始执行。
  3. join(timeout=None):等待线程完成。调用该方法会阻塞当前线程,直到被调用的线程执行完毕。可以使用timeout参数来设置最长等待时间。
  4. is_alive():检查线程是否仍然在运行。返回True表示线程仍在执行,返回False表示线程已经结束。
  5. setDaemon(daemonic):将线程设置为守护线程。守护线程是一种特殊的线程,当主线程退出时,它们会被强制结束。
  6. join()is_alive()通常用于等待和监视线程的状态切换。例如,在示例中使用了join()来等待线程完成,并使用is_alive()来检查线程是否仍在运行。

线程状态的切换和管理通常由Python解释器和操作系统来处理,开发人员主要使用上述函数来与线程进行交互,以控制线程的行为和等待线程完成。

# ---encoding:utf-8---
# @Time    : 2023/9/11 21:42
# @Author  : Darwin_Bossen
# @Email   :3139066125@qq.com
# @Site    :  生产者消费者模型
# @File    : Prouduct.py

import threading
import time
import random

# 共享的缓冲区
buffer = []
MAX_BUFFER_SIZE = 5

# 生产者函数
def producer():
    while True:
        item = random.randint(1, 100)  # 随机生成一个数据项
        if len(buffer) < MAX_BUFFER_SIZE:
            buffer.append(item)
            print(f"生产者生产了 {item}")
        time.sleep(random.uniform(0.1, 0.5))  # 模拟生产时间

# 消费者函数
def consumer():
    while True:
        if len(buffer) > 0:
            item = buffer.pop(0)
            print(f"消费者消费了 {item}")
        time.sleep(random.uniform(0.1, 0.5))  # 模拟消费时间

# 创建生产者和消费者线程
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

# 启动线程
producer_thread.start()
consumer_thread.start()

# 主线程等待子线程完成
producer_thread.join()
consumer_thread.join()


if __name__ == '__main__':
    pass
  • 在这个示例中,我们创建了一个共享的缓冲区buffer,并定义了一个最大缓冲区大小MAX_BUFFER_SIZE。然后,我们创建了一个生产者函数和一个消费者函数,它们分别模拟生产和消费数据的过程。生产者不断生成随机数据项并将其放入缓冲区,而消费者从缓冲区中取出数据项。
  • 我们创建了两个线程,一个用于生产者,一个用于消费者,然后启动它们。这两个线程会同时运行,并模拟生产者和消费者的交互过程。

2.3.4 线程同步

线程同步是在多线程编程中用于控制线程之间协同工作的重要机制,主要有以下几个原因:

  1. 共享数据访问:当多个线程同时访问和修改共享数据时,可能会导致数据不一致或损坏。线程同步机制确保在任何给定时刻只有一个线程可以访问共享数据,从而防止竞态条件(race conditions)和数据一致性问题。
  2. 避免竞态条件:竞态条件是多个线程在竞争相同资源时出现的问题。例如,两个线程同时尝试向一个共享计数器增加值,如果没有适当的同步,可能会导致计数器的值不正确。线程同步可以防止这种情况发生。
  3. 确保正确的执行顺序:在某些情况下,你可能需要确保线程按照特定的顺序执行。例如,在生产者-消费者问题中,生产者应该在消费者之前生产数据。使用线程同步可以控制线程的执行顺序。
  4. 资源管理:线程同步还用于管理共享资源的访问,如文件、网络连接或硬件设备。多个线程尝试同时访问这些资源可能会导致不稳定性或性能问题。
  5. 避免死锁:线程同步也有助于避免死锁,即多个线程相互等待对方释放资源的情况。通过合理设计同步机制,可以减少死锁的风险。
  6. 提高程序性能:虽然线程同步会引入一些开销,但它也可以允许多个线程并发执行,从而提高程序的性能。在合适的情况下,多线程可以更有效地利用多核处理器。

线程同步是多线程编程中的重要概念,它涉及到多个线程协调执行,以确保数据的一致性和正确性。以下是一些常见的线程同步机制和Python中的实现方式:

  1. 锁(Lock):锁是最基本的线程同步机制之一。它允许一个线程在进入关键区域(临界区)时获得锁,而其他线程必须等待锁被释放才能进入。Python中可以使用threading.Lock来创建锁对象。
import threading

lock = threading.Lock()

def example_function():
    with lock:
        # 临界区代码,只有一个线程可以执行此部分
        pass
  • 案例
# ---encoding:utf-8---
# @Time    : 2023/9/11 21:49
# @Author  : Darwin_Bossen
# @Email   :3139066125@qq.com
# @Site    :  Lock 锁 生产者消费者模型
# @File    : LockTest.py

import threading
import time
import random
# 共享的缓冲区
buffer = []
MAX_BUFFER_SIZE = 5
# 创建锁对象
lock = threading.Lock()
# 生产者函数
def producer():
    while True:
        item = random.randint(1, 100)  # 随机生成一个数据项
        lock.acquire()  # 获取锁
        if len(buffer) < MAX_BUFFER_SIZE:
            buffer.append(item)
            print(f"{ threading.current_thread()}-生产者生产了 {item}")
        lock.release()  # 释放锁
        time.sleep(random.uniform(0.1, 0.5))  # 模拟生产时间
# 消费者函数
def consumer():
    while True:
        lock.acquire()  # 获取锁
        if len(buffer) > 0:
            item = buffer.pop(0)
            print(f"{ threading.current_thread()}-消费者消费了 {item}")
        lock.release()  # 释放锁
        time.sleep(random.uniform(0.1, 0.5))  # 模拟消费时间
# 创建生产者和消费者线程
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)
# 启动线程
producer_thread.start()
consumer_thread.start()
# 主线程等待子线程完成
producer_thread.join()
consumer_thread.join()

if __name__ == '__main__':
    pass

  1. 条件变量(Condition):条件变量允许线程等待某个条件满足后再继续执行。它通常与锁一起使用,可以使用threading.Condition来创建条件变量对象。
import threading

condition = threading.Condition()

def producer():
    with condition:
        # 生产数据
        condition.notify()  # 通知消费者数据已准备好

def consumer():
    with condition:
        while not data_ready:
            condition.wait()  # 等待生产者通知数据已准备好
        # 消费数据
  • 案例
# ---encoding:utf-8---
# @Time    : 2023/9/11 21:54
# @Author  : Darwin_Bossen
# @Email   :3139066125@qq.com
# @Site    :  Condition 条件变量生产者消费者模型
# @File    : ConditionTest.py

import threading
import time
import random

# 共享的缓冲区
buffer = []
MAX_BUFFER_SIZE = 5

# 创建条件变量对象
condition = threading.Condition()

# 生产者函数
def producer():
    while True:
        item = random.randint(1, 100)  # 随机生成一个数据项
        condition.acquire()  # 获取锁
        if len(buffer) < MAX_BUFFER_SIZE:
            buffer.append(item)
            print(f"{ threading.current_thread()}-生产者生产了 {item}")
        condition.notify()  # 通知消费者线程
        condition.release()  # 释放锁
        time.sleep(random.uniform(0.1, 0.5))  # 模拟生产时间

# 消费者函数
def consumer():
    while True:
        condition.acquire()  # 获取锁
        if len(buffer) > 0:
            item = buffer.pop(0)
            print(f"{ threading.current_thread()}-消费者消费了 {item}")
        condition.wait()  # 等待生产者线程
        condition.release()  # 释放锁
        time.sleep(random.uniform(0.1, 0.5))  # 模拟消费时间


# 创建生产者和消费者线程
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

# 启动线程
producer_thread.start()
consumer_thread.start()

# 主线程等待子线程完成
producer_thread.join()
consumer_thread.join()


if __name__ == '__main__':
    pass

  1. 信号量(Semaphore):信号量用于控制同时访问共享资源的线程数量。它可以用来限制并发访问资源的线程数量。
import threading

semaphore = threading.Semaphore(3)  # 允许同时访问的线程数量为3

def example_function():
    with semaphore:
        # 最多有3个线程可以同时执行此部分
        pass
  • 案例
# ---encoding:utf-8---
# @Time    : 2023/9/11 21:57
# @Author  : Darwin_Bossen
# @Email   :3139066125@qq.com
# @Site    :  信号量 Semaphore 生产者消费者模型
# @File    : SemaphoreTest.py

import threading
import time
import random

# 共享的缓冲区
buffer = []
MAX_BUFFER_SIZE = 5

# 创建信号量对象
semaphore = threading.Semaphore(1)

# 生产者函数
def producer():
    while True:
        item = random.randint(1, 100)  # 随机生成一个数据项
        semaphore.acquire()  # 获取信号量
        if len(buffer) < MAX_BUFFER_SIZE:
            buffer.append(item)
            print(f"{ threading.current_thread()}-生产者生产了 {item}")
        semaphore.release()  # 释放信号量
        time.sleep(random.uniform(0.1, 0.5))  # 模拟生产时间

# 消费者函数

def consumer():
    while True:
        semaphore.acquire()  # 获取信号量
        if len(buffer) > 0:
            item = buffer.pop(0)
            print(f"{ threading.current_thread()}-消费者消费了 {item}")
        semaphore.release()  # 释放信号量
        time.sleep(random.uniform(0.1, 0.5))  # 模拟消费时间

# 创建生产者和消费者线程

producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

# 启动线程

producer_thread.start()
consumer_thread.start()

# 主线程等待子线程完成

producer_thread.join()
consumer_thread.join()


if __name__ == '__main__':
    pass

  1. 事件(Event):事件用于线程之间的通信,一个线程等待事件的状态为真,而另一个线程可以设置事件的状态为真。
import threading

event = threading.Event()

def thread1():
    event.wait()  # 等待事件变为真
    # 执行线程1的操作

def thread2():
    # 执行线程2的操作
    event.set()  # 设置事件为真,通知线程1
  • 案例
# ---encoding:utf-8---
# @Time    : 2023/9/11 21:59
# @Author  : Darwin_Bossen
# @Email   :3139066125@qq.com
# @Site    : Event 事件 生产者消费者模型
# @File    : EventTest.py

import threading
import time
import random

# 共享的缓冲区
buffer = []
MAX_BUFFER_SIZE = 5

# 创建事件对象
event = threading.Event()

# 生产者函数
def producer():
    while True:
        item = random.randint(1, 100)  # 随机生成一个数据项
        if len(buffer) < MAX_BUFFER_SIZE:
            buffer.append(item)
            print(f"{ threading.current_thread()}-生产者生产了 {item}")
        event.set()  # 设置事件
        time.sleep(random.uniform(0.1, 0.5))  # 模拟生产时间

# 消费者函数

def consumer():
    while True:
        if len(buffer) > 0:
            item = buffer.pop(0)
            print(f"{ threading.current_thread()}-消费者消费了 {item}")
        event.clear()  # 清除事件
        event.wait()  # 等待事件
        time.sleep(random.uniform(0.1, 0.5))  # 模拟消费时间

# 创建生产者和消费者线程

producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

# 启动线程

producer_thread.start()
consumer_thread.start()

# 主线程等待子线程完成

producer_thread.join()
consumer_thread.join()


if __name__ == '__main__':

    pass

搜索

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值