线程操作

  • 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()) # 线程名字

python sqlite3 多线程被锁_时间片


创建线程的目的是告诉它帮助我们做些什么,做些什么通过参数 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()

python sqlite3 多线程被锁_多线程_02

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 线程可能被连续调度,从而获得时间片。

python sqlite3 多线程被锁_时间片_03

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]

python sqlite3 多线程被锁_python_04


可以看见结果预想的一样。但如果中间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]

python sqlite3 多线程被锁_时间片_05

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]

python sqlite3 多线程被锁_python_06

看到,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]

python sqlite3 多线程被锁_锁机制_07


可知,实现的我们想要的结果,但是这已经是单线程顺序执行了,已经失去多线程的价值,并且还带来了因为线程创建开销,浪费时间的副作用。

程序中只有一把锁,通过 try...finally 还能确保不发生死锁。但是,当程序中启用多把锁,还是很容易发生死锁。

因此注意使用场合,避免死锁,是我们在使用多线程开发时需要注意的一些问题。