线程操作
- 1.1 线程的背景知识
- 1.2 创建线程
- 1.3 交替获得CPU时间片
- 1.4 多线程抢夺同一个变量
- 1.5 线程 加锁、解锁机制
1.1 线程的背景知识
import threading # 导入线程相关的模块
t = threading.current_thread() # 返回当前线程
print(t) # 主线程执行
print(t.getName()) # 线程名字
print(t.ident) # 线程ID
print(t.isAlive()) # 线程是否存活
1.2 创建线程
import threading # 导入线程相关的模块
my_thread = threading.Thread()
print(my_thread)
print(my_thread.getName()) # 线程名字
print(my_thread.ident) # 线程ID
print(my_thread.isAlive()) # 线程是否存活
# 重命名线程名字
my_thread = threading.Thread(name='my_thread')
print("重命名:",my_thread.getName()) # 线程名字
创建线程的目的是告诉它帮助我们做些什么,做些什么通过参数 target
传入,参数类型为 callable
,函数就是可调用的:
import threading
#具体做啥事,写在函数中
def run(number): #输出线程名与次数
print(threading.currentThread().getName(),number)
if __name__ == '__main__':
for i in range(10):
# 指明具体的方法和方法需要的参数
my_thread = threading.Thread(target=run, args=(i,))
# 启动线程
my_thread.start()
1.3 交替获得CPU时间片
开辟3个线程,装到 threads 中:
import time
from datetime import datetime
import threading
def print_time():
for _ in range(5): # 在每个线程中打印5次
time.sleep(0.1) # 模拟打印前的相关处理逻辑
print('当前线程%s,打印结束时间为:%s\n' % (threading.current_thread().getName(),datetime.today()))
threads = [threading.Thread(name='t%d'%(i,), target=print_time) for i in range(3)]
[t.start() for t in threads]
打印结果如下, t0 , t1 , t2 三个线程,根据操作系统的调度算法,轮询获得CPU时间片,注意观察,t0,t2 线程可能被连续调度,从而获得时间片。
1.4 多线程抢夺同一个变量
多线程编程,存在抢夺同一个变量的问题。假如创建的10个线程同时竞争全局变量 a。
import threading
a = 0
def add():
global a
a += 1 # 等差 +1
print("%s adds a to 1:%d"%(threading.current_thread().getName(), a))
threads = [threading.Thread(name = 't%d' %(i,), target=add) for i in range(10)]
print(threads)
[t.start() for t in threads]
可以看见结果预想的一样。但如果中间sleep沉睡,那会变得怎么样?
mport threading
import time
a = 0
def add():
global a
a += 1 # 等差 +1
time.sleep(0.2) # 延时0.2秒,模拟写入所需时间
print("%s adds a to 1:%d"%(threading.current_thread().getName(), a))
threads = [threading.Thread(name = 't%d' %(i,), target=add) for i in range(10)]
[t.start() for t in threads]
import threading
import time
a = 0
def add():
global a
tmp = a + 1 # 等差 +1
time.sleep(0.2) # 延时0.2秒,模拟写入所需时间
a = tmp
print("%s adds a to 1:%d"%(threading.current_thread().getName(), a))
threads = [threading.Thread(name = 't%d' %(i,), target=add) for i in range(10)]
[t.start() for t in threads]
看到,10个线程全部运行后, a 的值只相当于一个线程执行的结果。可以看见延时过后,CPU立即分配计算资源给其他线程,所以输出的进程顺序是无序的。直到分配给所有线程后,根据结果反映出,0.2秒的休眠时长内,程序已经运行完成,每个线程get到的a值都是 10 / 1 。
这个结果是我们不想的,我们通过python中提供的锁机制,某段代码只能单线程执行时,上锁,其他线程等待,直到释放锁后,其他线程再争锁,执行代码,释放锁,重复以上。
1.5 线程 加锁、解锁机制
可以利用locka = threading.Lock()
方法实现锁机制。通过执行 获得锁 locka.acquire()
,通过 locka.release()
释放锁。
import threading
import time
locka = threading.Lock()
a = 0
def add():
global a
try:
locka.acquire() # 获得锁
tmp = a + 1 # 等差 +1
time.sleep(0.2) # 延时0.2秒,模拟写入所需时间
a = tmp
finally:
locka.release() # 解放锁
print("%s adds a to 1:%d"%(threading.current_thread().getName(), a))
threads = [threading.Thread(name = 't%d' %(i,), target=add) for i in range(10)]
[t.start() for t in threads]
可知,实现的我们想要的结果,但是这已经是单线程顺序执行了,已经失去多线程的价值,并且还带来了因为线程创建开销,浪费时间的副作用。
程序中只有一把锁,通过 try...finally
还能确保不发生死锁。但是,当程序中启用多把锁,还是很容易发生死锁。
因此注意使用场合,避免死锁,是我们在使用多线程开发时需要注意的一些问题。