线程

线程的概念和作用

线程(Thread)是操作系统中进程内的执行单元,是调度和执行的基本单位。一个进程可以包含多个线程,这些线程共享进程的资源(如内存、文件句柄等),但每个线程有独立的执行路径(如栈、寄存器状态等)。

一、线程的核心概念

  1. 线程的定义
    • 线程是进程的子任务,是 CPU 调度的最小单位。
    • 一个进程至少包含一个线程(主线程),也可以创建多个线程。
  2. 线程与进程的关系
    • 进程是资源分配的单位,拥有独立的内存空间和系统资源。
    • 线程是执行任务的单位,共享进程的资源,但独立运行。
  3. 线程的特性
    • 轻量级:线程的创建、切换和销毁开销远小于进程。
    • 资源共享:线程共享进程的内存、文件描述符等资源,通信高效。
    • 并发性:多个线程可以并行执行(取决于 CPU 核数和系统支持)。

二、线程的核心作用

1. 提高程序效率(并发执行)

  • I/O 密集型任务:当线程等待 I/O 操作(如读写文件、网络请求)时,其他线程可以继续执行,提升整体效率。
  • 示例:在 Web 服务器中,每个客户端请求由一个线程处理,避免阻塞其他请求。

2. 实现多任务协作

  • 多个线程可以协作完成复杂任务,例如:
    • 一个线程负责数据生成,另一个线程负责数据处理。
    • GUI 应用中,主线程负责界面更新,子线程负责后台计算。

3. 资源共享与通信

  • 线程共享进程的内存和资源,可以直接访问全局变量和共享数据,通信更简单高效。
  • 示例:多个线程共同修改一个共享变量(需同步机制避免冲突)。

4. 简化程序设计

  • 在单进程中使用多线程,避免进程间通信(IPC)的复杂性。
  • 适合需要频繁交互的任务(如多线程爬虫、实时数据处理)。

多线程的使用

import threading
import time
def task1():
    for i in range(5):
        print(f"任务1 - 计数: {i}")
        time.sleep(1)
def task2():
    for i in range(5):
        print(f"任务2 - 计数: {i}")
        time.sleep(1)
def main():
    # 创建线程
    thread1 = threading.Thread(target=task1)
    thread2 = threading.Thread(target=task2)
    # 启动线程
    thread1.start()
    thread2.start()
    # 等待线程完成
    thread1.join()
    thread2.join()
    print("所有任务完成。")
if __name__ == "__main__":
    main()
线程

获取线程对象和线程名

获取对象:如果你需要在一个运行的线程内获取其对应的线程对象本身,Python 提供了一个方便的方法:threading.current_thread()。这个函数返回当前执行线程的 Thread 对象。你可以在任何线程内调用这个函数来获取当前线程的对象,并访问它的各种属性。

获取线程名:可以通过Thread对象的 name 属性来获取和设置线程名。此外,线程对象本身可以通过变量来进行访问

import threading
import time

def task():
    # 获取当前线程对象
    current_thread = threading.current_thread()
    # 输出线程名和线程对象
    print(f"当前线程对象: {current_thread}, 线程名: {current_thread.name}")
    for i in range(5):
        print(f"{current_thread.name} - 计数: {i}")
        time.sleep(1)

def main():
    # 创建线程并设置名称
    thread1 = threading.Thread(target=task, name="线程1")
    thread2 = threading.Thread(target=task, name="线程2")

    # 启动线程
    thread1.start()
    thread2.start()

    # 打印线程对象信息
    print(f"主线程中的线程1对象: {thread1}")
    print(f"主线程中的线程2对象: {thread2}")
    
    # 等待线程完成
    thread1.join()
    thread2.join()

    print("所有任务完成。")

if __name__ == "__main__":
    main()
线程

解释:

  • 获取当前线程: 在 task 函数中,使用 threading.current_thread() 来获取当前线程对象,并输出它的名称和对象本身。这会在每个线程启动后立即显示。
  • 在主线程中显示线程对象: 在 main 函数中,创建了 thread1 和 thread2,并在启动它们之后也打印了它们的对象信息。这显示了在主线程中访问线程对象时的信息。

线程执行带有参数的任务

同时使用 args 和 kwargs 传递参数的方式

import threading
import time

def task(name, count, delay=1):
    # 获取当前线程对象
    current_thread = threading.current_thread()
    # 输出线程名和线程对象
    print(f"当前线程对象: {current_thread}, 线程名: {current_thread.name}")
    for i in range(count):
        print(f"{current_thread.name} - {name} - 计数: {i}")
        time.sleep(delay)  # 使用 delay 来控制睡眠时间

def main():
    # 创建线程并设置名称和参数(使用 args)
    thread1 = threading.Thread(target=task, args=("任务1", 5), name="线程1")
    # 创建线程并设置名称和参数(使用 kwargs)
    thread2 = threading.Thread(target=task, kwargs={"name": "任务2", "count": 3, "delay": 2}, name="线程2")

    # 启动线程
    thread1.start()
    thread2.start()

    # 打印线程对象信息
    print(f"主线程中的线程1对象: {thread1}")
    print(f"主线程中的线程2对象: {thread2}")
    
    # 等待线程完成
    thread1.join()
    thread2.join()

    print("所有任务完成。")

if __name__ == "__main__":
    main()

解释:

  1. 任务函数task 接受三个参数:namecount 和 delay。其中 delay 是一个带默认值的参数。
  2. args:
    • args=("任务1", 5): 用于 thread1,传输顺序参数 name 和 count。使用默认的 delay 值。
  3. kwargs:
    • kwargs={"name": "任务2", "count": 3, "delay": 2}: 用于 thread2,传输命名参数 namecount 和 delay。这样可以覆盖默认的 delay 值。

线程的注意点

线程在执行时是无序的

我们创建多个线程,每个线程都会每隔一小段时间打印一条消息。这展示了线程执行时的不确定性和它们交替执行的效果:

import threading
import time

def task(identifier):
    for _ in range(3):
        time.sleep(0.1)  # 每个任务都短暂休眠
        print(f"线程 {identifier} 正在执行")

def main():
    threads = []
    for i in range(5):  # 创建5个线程
        thread = threading.Thread(target=task, args=(i,))
        threads.append(thread)

    for thread in threads:
        thread.start()  # 启动每个线程

    for thread in threads:
        thread.join()  # 等待所有线程完成

if __name__ == "__main__":
    main()
线程

解释:

  1. 随机休眠: 在 task 内使用 random.uniform(0.1, 0.5) 来随机地设置睡眠时间,这有助于模拟线程调度时的非确定性,增强观察效果。
  2. 多线程启动: 从 main 函数中启动多个线程,每个线程输出其名称和计数。这些输出会展示每个线程在执行时的交错。
  3. 无序执行: 由于线程几乎同时启动并随机休眠,你可以观察到每个线程几乎以无序方式输出说明当前正在哪个计数上迭代。

此代码例子展示了线程调度可能在不同时间片的执行顺序,并没有保证按特定顺序运行。

如果有一个线程执行报错,其他线程依然会执行

在多线程编程中,假设一个线程执行过程中遇到错误并引发异常,这并不会阻止其他线程的执行。每个线程是独立的,除非你的代码有机制让线程之间相互影响。

import threading
import time

def task(name, count, delay=1):
    try:
        # 获取当前线程对象
        current_thread = threading.current_thread()
        # 输出线程名和线程对象
        print(f"当前线程对象: {current_thread}, 线程名: {current_thread.name}")
        for i in range(count):
            if name == "任务1" and i == 2:
                raise ValueError("任务1遇到了一个问题")  # 模拟错误
            print(f"{current_thread.name} - {name} - 计数: {i}")
            time.sleep(delay)
    except Exception as e:
        print(f"{current_thread.name} - {name} 报错: {e}")

def main():
    # 创建线程并设置名称和参数
    thread1 = threading.Thread(target=task, args=("任务1", 5), name="线程1")
    thread2 = threading.Thread(target=task, args=("任务2", 5), name="线程2")

    # 启动线程
    thread1.start()
    thread2.start()

    # 打印线程对象信息
    print(f"主线程中的线程1对象: {thread1}")
    print(f"主线程中的线程2对象: {thread2}")
    
    # 等待线程完成
    thread1.join()
    thread2.join()

    print("所有任务完成。")

if __name__ == "__main__":
    main()
线程

解释:

  1. 异常处理: 在 task 函数中,使用 try...except 块来捕获可能出现的异常。在正常执行的过程中,任务1 会在计数到2时故意触发一个 ValueError。捕获此异常后,输出错误信息,但不会停止 任务2 的执行。
  2. 独立执行: 每个线程独立执行它们的代码。即使 任务1 出现异常,任务2 会继续正常运行。

发布者:LJH,转发请注明出处:https://www.ljh.cool/42277.html

(0)
上一篇 1天前
下一篇 2022年4月10日 下午6:35

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注