1、threading模块介绍
python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用
  • 创建线程对象:threading.Thread(target=func)
  • 参数target指定线程执行的任务 (函数)
Thread类提供了以下方法:
  • run():用以表示线程活动的方法
  • start():启动线程活动
  • join([time]):设置主线程会等待time秒后再往下执行,time默认为子线程结束,多个子线程之间设置的值会叠加
import time

def func1(name):
for i in range(5):
print("{}------正在做事情1------".format(name))
time.sleep(1)
# 线程切换

def func2(name):
for i in range(6):
print("{}-----正在做事情2------".format(name))
time.sleep(1)

"""
创建线程的参数:
target:指定线程执行的任务函数
name:设置线程名称
args:给任务函数传递参数(元组)
kwargs:给任务函数传递参数(字典)
daemon:是否设为守护线程
守护线程:主线程执行结束、子线程不管有没有执行完,都终止执行

线程对象的方法:
start():启动线程
join():设置主线程等待子线程执行的时间(默认等待执行完)
getName() # 获取线程名
is_alive() # 判断线程是否存活
isAlive()
isDaemon() # 判断是否为守护线程

python中线程释放全局解释器锁的两种状态(线程切换):
1、线程中执行遇到IO、耗时操作
2、线程执行的时达到指定的阈值(0.005秒)
# 获取线程切换的阈值
# import sys
# print(sys.getswitchinterval())

"""

# 多任务模式(多线程、多进程、协程去实现)
from threading import Thread

# 创建一个线程对象
t = Thread(target=func1, name='z1', args=('zyj1',))
t2 = Thread(target=func2, name='z2', kwargs={'name': "zyj2"})

# 获取线程名:
# print(t.getName())
# print(t2.getName())

# 启动线程执行
st = time.time()
t.start()
t2.start()

# 设置主线程等待子线程执行结束
t.join()
t2.join()

# 其他的方法
et = time.time()
print("执行时间为:", et - st)
多线程-共享全局变量
1、多线程之间修改全局变量
  • 线程之间是共用同一块内存的,那么线程可以共享全局变量
    • 案例:当前有一个全局变量 a=100, 再线程a中修改,线程b中是否会生效。
    • 列表当做实参传递到线程中,线程中修改会有说明影响 
  • 总结
    • 在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据** 
    • 缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安
2、多线程-共享全局变量的问题
如果多个线程同时对同一个全局变量操作,会出现资源竞争问题,从而数据结果会不正确
锁-Lock
  • 上面的bug如何解决?控制线程的执行,避免同时获取数据
  • 线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引⼊互斥锁。
  • 互斥锁为资源引⼊⼀个状态:锁定/⾮锁定。
  • 某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改直到该线
  • 程释放资源,将资源的状态变成“⾮锁定”,其他的线程才能再次锁定该资源
  • 互斥锁保证了每次只有⼀个线程进⾏写⼊操作,从⽽保证了多线程情况下数据的正确性
  • threading模块中定义了Lock类,可以方便的处理锁定
# 创建锁 
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 释放
mutex.release()
注意:
如果这个锁之前是没有上锁的,那么acquire不会堵塞
如果在调用acquire对这个锁上锁之前 它已经被 其他线程上了锁,那么此时acquire会堵塞,直到这个锁被解锁为止 
  • 上锁解锁过程
    • 当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态
    • 每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态
    • 线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态
  • 总结
    • 锁的好处:确保了某段关键代码只能由一个线程从头到尾完整地执行
    • 锁的坏处:
      • 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
      • 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁