多任务线程

线程的介绍

  • 介绍:多任务除了使用进程,还可以使用线程
  • 概念:线程是进程中执行代码的一个分支,每个执行分支要想工作执行代码需要cpu调度,也就是说线程是cpu的基本单位,每个进程至少有一个线程,而这个线程就是常说的主线程

线程的主要特性包括:

  • 轻量级:相比进程,线程的创建和销毁所需的资源更少,因此更轻量级。
  • 共享内存:线程共享其所属进程的地址空间和资源,如内存、文件句柄等。
  • 并发执行:多个线程可以在同一进程中并发执行,提高了程序的执行效率。

多线程的使用

1.导入线程模块 

import threading
2.参数说明
  • 线程可以执行带有参数的任务。
  • 线程的执行是无序的,由操作系统调度器决定。
  • 主线程通常会等待所有子线程结束后才结束,但也可以通过设置守护线程(daemon thread)来改变这一行为。
  • 线程之间共享全局变量,但这也可能导致数据同步问题。
# 1.导入线程模块
import threading
import time


def sing():
    # 获取当前线程
    current_thread = threading.current_thread()
    print("sing:", current_thread)
    for i in range(3):
        print('唱歌中...')
        time.sleep(0.2)

def dance():
    # 获取当前线程
    current_thread = threading.current_thread()
    print("dance:", current_thread)
    for i in range(3):
        print('跳舞中...')
        time.sleep(0.2)

# 2.创建子进程
if __name__ == '__main__':
    # 获取当前线程
    current_thread = threading.current_thread()
    print("main_thread:", current_thread)

    sing_thread = threading.Thread(target=sing)
    dance_thread = threading.Thread(target=dance)
    # 3.启动子线程执行对应的任务
    sing_thread.start()
    dance_thread.start()

运行结果:

main_thread: <_MainThread(MainThread, started 22364)>
sing: <Thread(Thread-1, started 4560)>
唱歌中...
dance: <Thread(Thread-2, started 18576)>
跳舞中...
唱歌中...
跳舞中...
跳舞中...
唱歌中...

线程执行带有参数的任务

import threading


def show_info(name,age):
    print("name: %s age: %d" % (name,age))


if __name__ == '__main__':
    # 创建子线程
    sub_thread = threading.Thread(target=show_info,args=('张三',20))
    sub_thread.start()

运行结果:

name: 张三 age: 20

线程之间执行是无序的

import threading
import time


def task():
    time.sleep(1)
    # 获取当前线程
    print(threading.current_thread())

if __name__ == '__main__':
    # 循环创建大量线程,测试线程之间执行是否无序
    for i in range(20):
        # 每循环一次创建一个子进程
        sub_thread = threading.Thread(target=task)
        # 启动子线程
        sub_thread.start()

运行结果:

<Thread(Thread-19, started 9276)><Thread(Thread-18, started 24564)>
<Thread(Thread-15, started 21668)>
<Thread(Thread-16, started 21720)>
<Thread(Thread-10, started 24780)>
<Thread(Thread-13, started 18592)>
<Thread(Thread-8, started 5932)>
<Thread(Thread-3, started 20580)>
<Thread(Thread-14, started 20520)>
<Thread(Thread-20, started 2848)>
<Thread(Thread-6, started 17120)><Thread(Thread-12, started 9300)><Thread(Thread-7, started 4216)>
<Thread(Thread-4, started 16280)>
<Thread(Thread-1, started 9092)><Thread(Thread-17, started 26008)>

<Thread(Thread-2, started 24492)>

<Thread(Thread-11, started 12292)>
<Thread(Thread-9, started 23596)><Thread(Thread-5, started 20896)>

主线程会等待子线程结束再结束

使用 join() 方法可以确保主线程等待指定的子线程结束后再继续执行。上述案例也展示了这一点。

线程之间共享全局变量

import threading

# 定义全局变量
g_list = []

# 添加数据的任务
def add_data():
    for i in range(3):
        # 每循环一次就把数据添加到全局变量
        g_list.append(i)
        print("add",i)
    # 代码执行到此说明添加数据完成
    print("添加数据完成")

# 读取数据任务
def read_data():
    print(g_list)

if __name__ == '__main__':
    # 创建添加数据的子线程
    add_thread = threading.Thread(target=add_data)
    # 创建读取数据的子线程
    read_thread = threading.Thread(target=read_data)

    # 启动进程
    add_thread.start()
    read_thread.start()

运行结果:

add 0
add 1
add 2
添加数据完成
[0, 1, 2]

线程之间共享全局变量数据出现错误问题

  竞态条件是由于两个或更多线程并发访问共享数据,并且至少有一个线程在访问过程中以某种方式修改数据而导致的。在上面的例子中,increment_global 和 decrement_global 函数可能会同时修改 global_var,这可能导致最终的结果不是预期的0。

解决竞态条件的方法

使用锁(如 threading.Lock)可以确保同一时间只有一个线程可以访问共享数据。

import threading  
  
global_var = 0  
lock = threading.Lock()  
  
def increment_global():  
    global global_var  
    for _ in range(100000):  
        with lock:  
            global_var += 1  
  
def decrement_global():  
    global global_var  
    for _ in range(100000):  
        with lock:  
            global_var -= 1  
  
# ...(与案例4中的代码相同,但包含锁)

互斥锁和死锁

  • 互斥锁(Mutex):用于保护共享资源,确保同一时间只有一个线程可以访问该资源。
  • 死锁(Deadlock):当两个或更多线程无限期地等待一个资源,而该资源又被另一个线程持有时,就发生了死锁。

进程和线程的对比

  • 进程:操作系统分配资源的基本单位,每个进程都有独立的内存空间和系统资源。
  • 线程:操作系统调度的基本单位,线程之间共享进程的资源,更轻量级,适合处理并发任务。
posted @ 2024-06-15 00:55  JJJhr  阅读(10)  评论(0编辑  收藏  举报