不是并行,不是真正意义上的并发,可以单核实现并发。进程是资源单位(相当于车间),线程是运行单位(相当于生产线)
io多的项目,多线程更优于多进程
1 threading
- 开启线程—函数
from threading import Thread
import time
def t_func(name, n):
time.sleep(n)
print("name:", name)
if __name__ == '__main__':
t = Thread(target=t_func, args=("lynn", 4))
t1 = Thread(target=t_func, args=("fancy", 1))
t.start()
t1.start()
t.join() # 线程t完全运行完,才继续往下运行
print("主")
注意:
target
是函数名字,不加()
args
是元组,必须按位置,只有一个参数时要加,
join
方法,不加join方法,是异步的,加join是把异步变成同步,就是只有该线程完全运行完,才继续往下运行,不影响其他线程。
- 开启线程—类
from threading import Thread
import time
class TClass(Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
time.sleep(1)
print("name:", self.name)
return self.name
if __name__ == '__main__':
t = TClass("lynn")
t1 = TClass("fancy")
t.start()
t1.start()
t.join()
print("主")
其他方法
getName()
线程的名字
setName()
设置线程的名字
isAlive()
返回线程是否活动的
- 守护线程
主线程所在的进程内,所有的线程运行完毕,停止运行。其实也是线程运行完毕,停止运行
from threading import Thread
import time
class TClass(Thread):
def __init__(self, n, name):
super().__init__()
self.name = name
self.n = n
def run(self):
time.sleep(self.n)
print("name:", self.name)
return self.name
if __name__ == '__main__':
t = TClass(1, name="lynn")
t1 = TClass(5, name="fancy")
t.daemon = True
t.start()
t1.start()
print("主")
注意:
是主线程所在的进程内所有的线程运行完毕,停止运行
daemon
必须在start
方法之前
2 线程数据安全和通信
线程锁
- 互斥锁
用来实现对共享资源的同步访问,也称为同步锁
同一时间只有一个进程对加锁的数据进行操作。把该部分变成串行,切不运行完,不释放锁,会一直阻塞。
from threading import Thread
from threading import Lock
import time
class TClass(Thread):
def __init__(self, n, name, lock):
super().__init__()
self.name = name
self.n = n
self.lock = lock
def run(self):
with self.lock:
with open("t_text.txt", "wt", encoding="utf-8")as f:
f.write(self.name)
time.sleep(self.n)
with open("t_text.txt", "rt", encoding="utf-8")as f:
print("name:", f.read())
print(self.name)
if __name__ == '__main__':
lock = Lock()
t = TClass(1, name="lynn", lock=lock)
t1 = TClass(5, name="fancy", lock=lock)
t.daemon = True
t.start()
t1.start()
print("主")
注意:
GIL锁也是互斥锁,是解释器级别的互斥锁
尽量只在修改数据的部分加锁,因为会把并发转为串行,会影响效率
- 死锁
两个或两个以上的线程(进程),在运行过程中两个线程(进程)互相等待,两个锁互相拿着没释放,没有外部原因,会一直阻塞,称为死锁现象。
死锁现象
import time
from threading import Thread
from threading import Lock
a_lock = Lock()
b_lock = Lock()
class DClass(Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
if self.name == "lynn":
self.a_func()
if self.name == "fancy":
self.b_func()
def a_func(self):
a_lock.acquire()
time.sleep(1)
print("拿到a锁")
b_lock.acquire()
print("拿到b锁")
a_lock.release()
print("释放a锁")
b_lock.release()
print("释放b锁")
def b_func(self):
b_lock.acquire()
time.sleep(1)
print("拿到b锁")
a_lock.acquire()
print("拿到a锁")
b_lock.release()
print("释放b锁")
a_lock.release()
print("释放a锁")
dc1 = DClass("lynn")
dc2 = DClass("fancy")
dc1.start()
dc2.start()
注意:
线程A(进程) 拿着 锁a,要拿锁b释放锁a
进程B(进程) 拿着 锁b,要拿锁a释放锁b
锁a和锁b形成死锁现象
- 递归锁RLock
解决死锁现象,针对多个锁的情况,递归锁可以被单个线程(进程)拿多次,每拿一次做一次标记,释放一次减去一个标记,标记为0时,才能被其他线程(进程)拿
import time
from threading import Thread
from threading import RLock
r_lock = RLock()
class DClass(Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
if self.name == "lynn":
self.a_func()
if self.name == "fancy":
self.b_func()
def a_func(self):
r_lock.acquire()
print("拿到a锁")
r_lock.acquire()
print("拿到b锁")
r_lock.release()
print("释放a锁")
time.sleep(1)
r_lock.release()
print("释放b锁")
def b_func(self):
r_lock.acquire()
time.sleep(1)
print("拿到c锁")
r_lock.acquire()
print("拿到c锁")
r_lock.release()
print("释放c锁")
r_lock.release()
print("释放c锁")
dc1 = DClass("lynn")
dc2 = DClass("fancy")
dc1.start()
dc2.start()
注意:
只有被该线程(进程)全部释放才能被别的线程拿
线程间的通信
线程时相互独立的,数据是隔离的
- Queue
管道:生产者消费者模型
from queue import Queue
from threading import Thread
import time
class SClass(Thread):
def __init__(self, Q, name):
super().__init__()
self.Q = Q
self.name = name
def run(self):
for i in range(100):
self.Q.put("{}的{}包子".format(self.name, i))
class XClass(Thread):
def __init__(self, Q):
super().__init__()
self.Q = Q
def run(self):
while True:
time.sleep(0.1)
res = self.Q.get()
print(res)
if not res:
break
if __name__ == '__main__':
Q = Queue(10)
st = SClass(Q, "lynn")
xc = XClass(Q)
st.start()
xc.start()
st.join()
Q.put(None)
3 ThreadPoolExecutor
支持线程池和进程池,python3.2之后版本
from concurrent.futures import ThreadPoolExecutor
import time
def thread_func(a):
time.sleep(2)
print("a")
if __name__ == '__main__':
with ThreadPoolExecutor(max_workers=5)as t:
res = t.submit(thread_func, 1)
# print(res.result()) # 会阻塞
r1 = t.submit(thread_func)
print("end")
# time.sleep(2)
print(res.done())
print(res.result())
print(res.done())
注意:
submit
方法,不阻塞,是异步的,第一个参数是方法名,后边按位置参数传方法需要的参数
with
会等所有的线程运行完毕,才继续往下运行
done()
查看线程的运行状态,True为运行完毕
result()
线程的返回值,会阻塞
- wait
开启多个线程
import time
from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED
class TClass:
@staticmethod
def run():
time.sleep(10)
print("ok")
if __name__ == '__main__':
with ThreadPoolExecutor(max_workers=5)as t:
t_list = (t.submit(TClass.run), )
wait(t_list, timeout=0.1, return_when=ALL_COMPLETED)
print('end')
注意:
第一个参数必须是可迭代对象,最好是元组,里边的元素是submit()
方法提交的数据
timeout
超时时间,超过这个时间,该方法的阻塞时间,默认线程运行完才会继续往下运行
- as_completed
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
class TClass:
@staticmethod
def run(a, b):
time.sleep(3)
print("ok", a, b)
return {"name": "lynn"}
if __name__ == '__main__':
with ThreadPoolExecutor(max_workers=5)as t:
t_list = [t.submit(TClass.run, 1, 2) for i in range(5)]
res_list = as_completed(t_list) # 阻塞
for i in res_list:
print(i.result()) # 返回值
注意:
as_completed
参数是可迭代对象:列表、元组等,元素是submit
方法处理的线程
for循环能取到每个线程的结果
- map
import time
from concurrent.futures import ThreadPoolExecutor
class TClass:
@staticmethod
def run(a, b):
time.sleep(1)
print('ok', a, b)
return a, b
if __name__ == '__main__':
with ThreadPoolExecutor(max_workers=5)as t:
genera_res = t.map(TClass.run, (1,3), (2,3)) # 结果是生成器
for i in genera_res:
print(i) # 线程的返回值
注意:
map
参数直接是方法,不用submit
参数以元组方式传递,多个参数多个元组,一个元组中多个参数表示调用多次