多任务线程
线程的介绍
- 介绍:多任务除了使用进程,还可以使用线程
- 概念:线程是进程中执行代码的一个分支,每个执行分支要想工作执行代码需要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):当两个或更多线程无限期地等待一个资源,而该资源又被另一个线程持有时,就发生了死锁。
进程和线程的对比
- 进程:操作系统分配资源的基本单位,每个进程都有独立的内存空间和系统资源。
- 线程:操作系统调度的基本单位,线程之间共享进程的资源,更轻量级,适合处理并发任务。