在介绍Python中的线程之前,先明确一个问题,Python中的多线程是假的多线程!
为什么这么说,我们先明确一个概念,全局解释器锁(GIL)
什么是GIL
Python代码的执行由Python虚拟机(解释器)来控制,同时只有一个线程在执行。对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同时只有一个线程在运行。
为什么要GIL
为了线程间数据的一致性和状态同步的完整性,(例如:线程2需要线程1执行完成的结果,然而线程2又比线程1执行时间短,线程2执行完成,线程1仍然还在执行,这就是数据的同步性)
GIL的影响
只有一个线程在运行,无法使用多核。
-
在多线程环境中,Python虚拟机按照以下方式执行。
1.设置GIL。
2.切换到一个线程去执行。
3.运行。
4.把线程设置为睡眠状态。
5.解锁GIL。
6.再次重复以上步骤。
比方我有一个4核的CPU,那么这样一来,在单位时间内每个核只能跑一个线程,然后时间片轮转切换。
但是Python不一样,它不管你有几个核,单位时间多个核只能跑一个线程,然后时间片轮转。
执行一段时间后让出,多线程在Python中只能交替执,10核也只能用到1个核
例如:
from threading import Thread
def loop():
while True:
print("亲爱的,我错了,我能吃饭了吗?")
if __name__ == '__main__':
for i in range(3):
t = Thread(target=loop)
t.start()
while True:
pass
而如果我们变成进程呢?cpu –100%
from multiprocessing import Process
def loop():
while True:
print("亲爱的,我错了,我能吃饭了吗?")
if __name__ == '__main__':
for i in range(3):
t = Process(target=loop)
t.start()
while True:
pass
多线程怎么使用多核
- 1、重写python编译器(官方cpython)如使用:PyPy解释器
- 2、调用C语言的链接库
cpu密集型(计算密集型)、I/O密集型
- 计算密集型任务由于主要消耗CPU资源,代码运行效率至关重要,C语言编写
- IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成99%的时间花费在IO上,脚本语言是首选,C语言最差。
2、创建多线程
def doSth(arg):
threadName = threading.current_thread().getName()
tid = threading.current_thread().ident
for i in range(5):
print("%s *%d @%s,tid=%d" % (arg, i, threadName, tid))
time.sleep(2)
1、使用_thread.start_new_thread开辟子线程
def simpleThread():
_thread.start_new_thread(doSth, ("拍森",))
mainThreadName = threading.current_thread().getName()
print(threading.current_thread())
for i in range(5):
print("劳资是主线程@%s" % (mainThreadName))
time.sleep(1)
while True:
pass
2、 通过创建threading.Thread对象实现子线程
def threadingThread():
t = threading.Thread(target=doSth, args=("大王派我来巡山",))
t.start()
t.join()
3、通过继承threading.Thread类,进而创建对象实现子线程
class MyThread(threading.Thread):
def __init__(self, name, task, subtask):
super().__init__()
self.name = name
self.task = task
self.subtask = subtask
def run(self):
for i in range(5):
print("[%s]并[%s] *%d @%s" % (self.task, self.subtask, i, threading.current_thread().getName()))
time.sleep(2)
def classThread():
mt = MyThread("小分队I", "巡山", "扫黄")
mt.start()
4、几个重要的API
并行 : 多个任务同时进行,但python多线程不允许,多进程是允许的
并发 : 多个任务在单个CPU交替执行 ,
串行 : 任务在CPU之间快速切换 , 交替执行
![]()
““python
def importantAPI():
print(threading.currentThread()) # 返回当前的线程变量
# 创建五条子线程
t1 = threading.Thread(target=doSth, args=(“巡山”,))
t2 = threading.Thread(target=doSth, args=(“巡水”,))
t3 = threading.Thread(target=doSth, args=(“巡鸟”,))
t1.start() # 开启线程
t2.start()
t3.start()
print(t1.isAlive()) # 返回线程是否活动的
print(t2.isDaemon()) # 是否是守护线程
print(t3.getName()) # 返回线程名
t3.setName("巡鸟") # 设置线程名
print(t3.getName())
print(t3.ident) # 返回线程号
# 返回一个包含正在运行的线程的list
tlist = threading.enumerate()
print("当前活动线程:", tlist)
# 返回正在运行的线程数量(在数值上等于len(tlist))
count = threading.active_count()
print("当前活动线程有%d条" % (count))
““
3、线程冲突
'''
【线程冲突】示例:
多个线程并发访问同一个变量而互相干扰
互斥锁
状态:锁定/非锁定
#创建锁
lock = threading.Lock()
#锁定
lock.acquire()
#释放
lock.release()
'''
'''
互相锁住对方线程需要的资源,造成死锁局面
递归锁,用于解决死锁的问题,可重复锁
'''
import threading
import time
money = 0
def addMoney():
global money
for i in range(1000000):
money += 1
print(money)
lock = threading.Lock()
def addMoneyWithLock():
time.sleep(1)
global money
with lock:
for i in range(1000000):
money += 1
print(money
def conflictDemo():
for i in range(5):
t = threading.Thread(target=addMoney)
t.start()
def handleConflictBySync():
for i in range(5):
t = threading.Thread(target=addMoney)
t.start()
t.join()
def handleConflictByLock():
for i in range(5):
t = threading.Thread(target=addMoneyWithLock)
t.start()
if __name__ == '__main__':
handleConflictByLock()
4、使用Semaphore调度线程:控制最大并发量
'''
使用Semaphore调度线程:控制最大并发量
'''
import threading
import time
sem = threading.Semaphore(3)
def doSth(arg):
with sem:
tname = threading.current_thread().getName()
print("%s正在执行【%s】" % (tname, arg))
time.sleep(1)
print("-----%s执行完毕!-----\n" % (tname))
time.sleep(0.1)
if __name__ == '__main__':
for i in range(10):
threading.Thread(target=doSth, args=("巡山",), name="小分队%d" % (i)).start()
pass