线程
进程是操作系统调度的最小单位,一个进程最少有一个主线程,一个进程中可以开启多个线程
真正干活的是线程
具体类似于:
有一个食谱(线程)上面写好了整个做菜流程,做菜中间需要几个食材和佐料(数据),然后人按照食谱做菜,按照食谱做菜这一整个过程可以看做是个进程
进程是资源分配的最小单位,线程是cpu调度的最小单位
线程开启的花销远远小于进程,但是使用多进程可以利用多核的优势,多线程只能调用同一个核
开启线程
方法和开启进程类似
from threading import Thread
def task():
print('我是子线程')
if __name__ == '__main__':
t = Thread(target=task)
t.start()
print('这是主线程')
GIL锁
在python的设计中,在主循环内同时只能有一个线程在执行,在python解释器中可以并行多个线程
对python解释器的访问由全局解释器锁GIL来控制,正是这个锁能保证同一时刻只有一个线程在运行
1.GIL锁它是在python解释器中的,只在cpython中有,pypy解释器是没有GIL锁的
2.起一个垃圾回收线程,一个是正常执行的线程,如果有一个线程即将被垃圾回收线程回收时被正常执行的线程调用,数据就会不安全
3.这个时候就设置了一把锁(GIL锁),一个线程要想执行,必须拿到这把锁
4.同一时刻,开启一个进程,一个进程中可以有多个线程,但是只能有一个线程执行
计算密集型:开进程
# 计算密集型是计算多,是由cpu进行计算,用进程可以充分发挥cpu的多核优势
io密集型:开线程
# io密集不牵扯计算,中间切换线程的消耗远远小于切换进程的消耗
进程与线程的比较
线程
from threading import Thread
import time
def task():
print('我是子线程')
time.sleep(1)
if __name__ == '__main__':
first = time.time()
t = Thread(target=task)
t.start()
t.join()
print('这是主线程')
print(time.time() - first)
# 我是子线程
# 这是主线程
# 1.0059137344360352
进程
from multiprocessing import Process
import time
def task():
print('我是子进程')
time.sleep(1)
if __name__ == '__main__':
first = time.time()
t = Process(target=task)
t.start()
t.join()
print('这是主进程')
print(time.time() - first)
# 我是子进程
# 这是主进程
# 1.0809822082519531
进程之间切换的速度相比线程,简直像过年一样
线程之间的数据是共享的
from threading import Thread
def task():
global n
n = 3
if __name__ == '__main__':
n = 0
t = Thread(target=task)
t.start()
t.join()
print(n)
# 3
Thread类的其他方法
is_alive() # 返回线程是否活动
getName() # 返回线程名
setName() # 设置线程名
join() # 与进程的join一样
threading.currentThread()
# 返回当前的线程变量
threading.enumerate()
# 返回一个包含正在运行的线程的list
threading.activeCount()
# 返回正在运行的线程数量,与len(threading.enumerate())结果一致
守护线程
setDaemon(True) # 开启守护线程,主线程结束,子线程跟着结束,需要放在start前面
from threading import Thread
import time
def task():
time.sleep(1)
print('我是子线程')
if __name__ == '__main__':
t = Thread(target=task)
t.setDaemon(True)
t.start()
print('主线程')
# 主进程
互斥锁(同步锁)
from threading import Thread,Lock
import time
def task(lock):
lock.acquire() # 上锁
global n
temp = n
time.sleep(1)
n = temp - 1
lock.release() # 释放锁
if __name__ == '__main__':
n = 10
ll = []
lock = Lock()
for i in range(3):
t = Thread(target=task, args=(lock, ))
t.start()
ll.append(t)
for j in ll:
j.join()
print("主线程:", n)
# 主线程: 7
如果没有同步锁,在time.sleep(1)之后的一瞬间,所有线程都会取n的值然后进行计算,会产生并发线程的数据安全问题
信号量
from threading import Thread,Semaphore
import time
def task(i, sm):
sm.acquire() # 上锁
print("%s:这个人开始上厕所了" % i)
time.sleep(3)
print("%s:这个人上完了" % i)
sm.release() # 释放锁
if __name__ == '__main__':
sm = Semaphore(2) # 允许两个线程同时执行
for i in range(10):
t = Thread(target=task, args=(i, sm))
t.start()
Semaphore:信号量可以理解为多把锁,同时允许多个线程来更改数据
Event事件
from threading import Thread, Event
import time
def girl(event):
print("都女士正在恋爱中...")
time.sleep(3)
print("都女生分手了")
event.set() # 发出分手的信号
def boy(i, event):
print("渣男:%s正在等待都女生分手" % i)
event.wait() # 等待信号
print("渣男:%s开始追了" % i)
if __name__ == '__main__':
event = Event()
t = Thread(target=girl, args=(event,))
t.start()
for i in range(10):
b_t = Thread(target=boy, args=(i, event))
b_t.start()