1、线程特征
线程特征:
1、一个进程可包含多个线程
2、一个进程中的所有线程共享这个进程的资源
3、多个线程的运行互不影响
4、线程的创建和销毁消耗的资源远小于进程
5、各个线程也有自己的ID等特征
2、threading模块创建线程
threading模块创建线程:
1、创建线程对象:
from threading import Thread
t = Thread()
功能:创建线程对象
参数:target绑定线程函数
args:元组,给线程函数位置传参
kwargs:字典,给线程函数键值传参
2、启动线程:
t.start()
3、回收线程:
t.join([timeout])
示例如下:
import time
from threading import Thread
from time import sleep
def func(sec, name):
sleep(sec)
print("%s已执行" % name)
#创建多个线程来执行func,并统计执行时间
start = time.time()
th = [] #创建列表存放线程对象,统一回收
for i in range(5):
t = Thread(target=func, args=(2,), kwargs={"name":"T%d" % i})
th.append(t)
t.start()
for i in th:
i.join()
print("执行时间:%fs" % (time.time() - start))
运行结果:
T2已执行T1已执行
T0已执行
T3已执行T4已执行
执行时间:2.010124s
4、线程间通信
1、通信方法:线程间使用全局变量进行通信
2、共享资源争夺:
共享资源:多个进程或线程都可以操作的资源称为共享资源,对共享资源的操作代码段称为临界区
影响:对共享资源的无序操作可能会带来数据的混乱,或者操作错误,此时往往需要同步互斥机制协调操作顺序
3、同步互斥机制:
同步:同步是一种协作关系,为完成操作,多进程或线程间形成一种协调,按照必要的步骤有序执行操作
互斥:互斥是一种制约关系,当一个进程或线程占有资源时会进行加锁处理,此时其他进程线程就无法操作该资源,直到解锁后才能操作。
4、线程同步互斥方法:
线程Event
from Threading import Event
e = Event() #创建线程Event对象
e.wait([timeout]) #阻塞等待e被set
e.set() #设置e,使wait结束阻塞
e.clear() #使e回到未被设置状态
e.is_set() #查看当前e是否被设置
线程锁Lock:
from threading import Lock
lock = Lock() #创建锁对象
lock.acquire() #上锁,如果lock已经上锁再调用会阻塞
lock.release() #解锁
with lock: #上锁
...
...
with代码块结束自动解锁
Event示例:
以下代码中,想要实现先设置全局变量str1的值,再打印输出:
from threading import Thread,Event
from time import sleep
str1 = "abcdefg"
e = Event()
def set_str():
sleep(0.1)
global str1
str1 = "1234567"
t = Thread(target=set_str)
t.start()
print(str1)
t.join()
运行结果如下:
abcdefg
可以看到,打印的结果还是未被设置的值,要想打印设置之后的值,修改代码如下:
from threading import Thread,Event
from time import sleep
str1 = "abcdefg"
e = Event()
def set_str():
sleep(0.1)
global str1
str1 = "1234567"
e.set() # 完成赋值后,设置e,使wait状态结束
t = Thread(target=set_str)
t.start()
e.wait() # 阻塞等待e被set
print(str1)
t.join()
结果如下:
1234567
线程锁示例:
from threading import Thread,Lock
num = 0
def add():
with lock: # 多个线程操作同一个全局变量时,上锁防止数据混乱
global num
for i in range(1000000):
num += 1
def sub():
lock.acquire() # 多个线程操作同一个全局变量时,上锁防止数据混乱
global num
for i in range(1000000):
num -= 1
lock.release()
if __name__ == "__main__":
lock = Lock() # 创建锁对象
t1 = Thread(target=add)
t2 = Thread(target=sub)
t1.start()
t2.start()
t1.join()
t2.join()
print(num)
运行结果:
0
5、死锁
死锁:两个或两个以上的线程在执行过程中,由于竞争资源或由于彼此通信而造成的一种阻塞现象,若无外力作用,他们都将无法推进下去,此时系统处于死锁状态或系统产生了死锁。
T1使用R1,请求R2;T2使用R2,请求R1
死锁产生条件:
当前线程拥有其他线程需要的资源
当前线程等待其他线程已拥有的资源
都不放弃自己拥有的资源
6、python线程GIL(全局解释器)
GIL:PYTHON解释器中加入了解释器锁,导致python解释器同一时间只能执行一个线程,降低了线程执行效率
后果:遇到阻塞时,线程会主动让出解释器,去解释其他线程,所以python多线程在执行多阻塞高延迟IO时可以提升程序效率,其他情况并不能对效率有所提升。
GIL问题建议:
尽量使用进程完成无阻塞的并发行为
不使用C作为解释器
结论:在无阻塞状态下,多线程程序和单线程程序执行效率几乎差不多,甚至还不如单线程效率,但是多进程运行相同内容却可以有明显效率提升。
7、进程和线程区别和联系
进程和线程区别和联系:
1、进程的创建和删除消耗的资源比线程多
2、进程空间独立,数据互不干扰,有专门通信方法;线程使用全局变量进行通信
3、一个进程可以有多个分支线程,两者有包含关系
4、多个线程共享进程资源,在共享资源操作时往往需要同步互斥处理