Python 多线程 threading
线程是CPU分配资源的基本单位,也就是说线程是程序执行的最小单位。
主线程:
当一个程序开始运行,这个程序就变成了一个进程,同时也会开启一个线程,这个线程就是程序的主线程。一个进程相当于一个或者多个线程。
当没有多线程编程时,一个进程相当于一个主线程;当有多线程编程时,一个进程包含多个线程(含主线程)。
子线程:
一个子线程可以看做是程序执⾏的⼀条分⽀,⼦线程启动后会和主线程同时执⾏。
1.单线程和多线程
线程是程序执行的最小单位,如果有多个任务要执行,单线程只能一个任务执行完再执行下一个任务,而多线程可以同时执行多个任务。
Python中可以使用内置的 threading 模块实现多线程。
(1)单线程
import time def func1(): for i in range(3): time.sleep(1) print("函数1--%s" % i) def func2(name): for i in range(3): time.sleep(1) print("函数2,你好,%s --%s" % (name, i)) func1() func2("sam") # 程序按顺序执行,func1执行完之后才会执行func2 # 函数1--0 # 函数1--1 # 函数1--2 # 函数2,你好,sam --0 # 函数2,你好,sam --1 # 函数2,你好,sam --2
(2)多线程
# -*- coding: utf-8 -*- import threading, time def func1(): for i in range(3): time.sleep(1) print("函数1--%s" % i) def func2(name): for i in range(3): time.sleep(1) print("函数2,你好,%s --%s" % (name, i)) if __name__ == '__main__': # 创建1个子线程,传入函数名作为参数 t1 = threading.Thread(target=func1) # 创建1个子线程,如果传入的函数带有参数,参数以元组的形式传入 t2 = threading.Thread(target=func2, args=("sam",)) # 启动线程 t1.start() t2.start() # 子线程启动后会和主线程同时执行。 for i in range(3): time.sleep(1) print("主线程") # 由于cpu调度不同,多次运行的输出结果可能不一样,本次输出结果如下: # 函数2,你好,sam --0函数1--0 # # 主线程 # 函数2,你好,sam --1主线程函数1--1 # # # 函数2,你好,sam --2函数1--2 # # 主线程
2.通过继承的方式实现多线程
继承 Thread 类,重新run()方法也可以实现多进程。
# -*- coding: utf-8 -*- import threading, time class Thread_Func1(threading.Thread): def run(self): for i in range(1, 4): time.sleep(1) print(f"执行子线程1---{i}") class Thread_Func2(threading.Thread): def run(self): for i in range(1, 4): time.sleep(1) print(f"执行子线程2---{i}") if __name__ == '__main__': t1 = Thread_Func1() t2 = Thread_Func2() t1.start() t2.start() # 多次运行的输出结果可能不一样,本次结果如下: # 执行子线程2---1执行子线程1---1 # # 执行子线程2---2 # 执行子线程1---2 # 执行子线程2---3 # 执行子线程1---3
3.线程的常用方法
- threading.current_thread() : 获取当前线程对象
- threading.enumerate(): 获取当前的所有线程信息
- 线程对象.name: 获取线程名称,赋值可修改线程名称
(1)获取当前线程对象
import threading, time class Thread_Func1(threading.Thread): def run(self): print(threading.current_thread()) # <Thread_Func1(Thread-1, started 54484)> for i in range(1, 4): time.sleep(1) print(f"执行子线程1---{i}") class Thread_Func2(threading.Thread): def run(self): print(threading.current_thread()) # <Thread_Func2(Thread-2, started 42216)> for i in range(1, 4): time.sleep(1) print(f"执行子线程2---{i}") if __name__ == '__main__': t1 = Thread_Func1() t2 = Thread_Func2() print(threading.current_thread()) # <_MainThread(MainThread, started 26196)> t1.start() t2.start()
(2)获取线程信息
import threading, time class Thread_Func1(threading.Thread): def run(self): for i in range(1, 4): time.sleep(1) print(f"执行子线程1---{i}") class Thread_Func2(threading.Thread): def run(self): for i in range(1, 4): time.sleep(1) print(f"执行子线程2---{i}") if __name__ == '__main__': t1 = Thread_Func1() t2 = Thread_Func2() print(threading.enumerate()) # 此时子线程未启动,只有主线程 # <_MainThread(MainThread, started 26196)> t1.start() t2.start() print(threading.enumerate()) # 启动了2个子线程,线程信息如下 # [<_MainThread(MainThread, started 5224)>, <Thread_Func1(Thread-1, started 44416)>, <Thread_Func2(Thread-2, started 52096)>]
(3)获取和修改线程名
import threading, time class Thread_Func1(threading.Thread): def run(self): thread = threading.current_thread() print(thread.name) # Thread-1 thread.name = "子线程" # 修改当前线程名 print(thread.name) # 子线程 for i in range(1, 4): time.sleep(1) print(f"执行子线程1---{i}") if __name__ == '__main__': thread_main = threading.current_thread() t1 = Thread_Func1() print(thread_main.name) # MainThread t1.start() print(threading.enumerate())
4.线程共享全局变量时的资源竞争
多个线程共享全局变量时,可能会发生多个线程同时对一个全局变量进行操作的情况。
import threading num = 100 def func1(): global num for i in range(100): if num >= 0: print(f"func1: num={num}") num -= 1 def func2(): global num for i in range(100): if num >= 0: print(f"func2: num={num}") num -= 1 if __name__ == '__main__': t1 = threading.Thread(target=func1) t2 = threading.Thread(target=func2) t1.start() t2.start()
上述代码运行时可能会出现如下结果:

如图所示,2个线程同时访问了全局变量num,所以都输出变量值为66。
5.锁
当多个线程同时对1个全局变量进行操作时,可能会发生数据不准确的问题。
比如有全局变量num = 10,线程1将num值加5再赋值给num,线程2将num值加10再赋值给num,我们希望的执行顺序如下:
- 1. 线程1 读取到num为10,将num加5,完成后num=15
- 2. 线程2 读取到num为15,将num加10,完成后 num=25
由于多线程可能出现的同时操作全局变量问题,程序执行可能如下:
- 1. 线程1和线程2同时读取到num为10
- 2. 线程1将num加5再赋值给num,num应该为15,
- 3. 由于线程2和线程1同时读取的num值,线程1对num做了修改,但线程2还不知道,操作进行到给num值加10,再赋值给num,最终num值为20
为了解决多线程共享资源可能导致的数据不准确的问题,我们可以给各线程相关操作加上锁。
线程1线程2都要对一个全局变量num进行操作,线程1对num操作前加上锁,操作完再释放锁,同样,线程2对num操作前也加上同一个锁,操作完再释放锁。
线程1和线程2执行到加锁的代码时,假如线程1先获得了锁,那么线程1会一直执行,直到释放锁之后,线程2才会再次和线程1争夺锁,谁抢到了锁谁才能继续执行加锁部分的代码。
也就是说,多个线程都加锁,谁先获得锁,谁就能一直执行到释放锁,在释放锁之前,其他加锁的进程不会执行。
示例1:
import threading lock = threading.Lock() num = 100 def func1(): global num lock.acquire() # 循环执行前加锁 for i in range(100): if num > 0: print(f"func1: num={num}") num -= 1 lock.release() # 循环之后完成后才释放锁 def func2(): global num for i in range(100): lock.acquire() # 每次循环就加锁 if num > 0: print(f"func2: num={num}") num -= 1 lock.release() # 一次循环结束就释放锁 if __name__ == '__main__': t1 = threading.Thread(target=func1) t2 = threading.Thread(target=func2) t1.start() t2.start()
上述代码,func1在循环开始前加了锁,在循环结束后才释放锁,所以上述代码会在func1都执行完后才会执行func2,也就是会一直输出func1的打印内容,func1执行完后,num = 0,func2执行没有打印输出内容。
注意,一定要是同一个锁。上述代码,如果func1和func2使用不同的锁对象加锁,那么相当于没加锁。
示例2:
import threading lock = threading.Lock() num = 100 def func1(): global num for i in range(100): lock.acquire() # 每次循环加锁 if num > 0: print(f"func1: num={num}") num -= 1 lock.release() # 一次循环结束就释放锁 def func2(): global num for i in range(100): lock.acquire() if num > 0: print(f"func2: num={num}") num -= 1 lock.release() if __name__ == '__main__': t1 = threading.Thread(target=func1) t2 = threading.Thread(target=func2) t1.start() t2.start()
上述代码,2个子进程都是在每次循环开始才加锁,1次循环结束就释放锁,所以是多num交替修改(可能1个线程连续修改多次,然后另一个线程才获取到锁)。
浙公网安备 33010602011771号