在说怎么创建线程之前,我觉得有必要说明一下GIL是什么,GIL的全称是 global interpreter lock,也就是全局解释器锁。
由于GIL的存在,使得每一时刻,只能有一个线程在一个CPU上执行,也就是说,我们没办法利用多核的优势,因为即使多核,
同一时刻也只能有一个线程在一个CPU上执行。这也是python被人吐槽慢的原因。
当然了,既然是锁,那么就自然会有释放的时候,GIL也不例外。GIL会根据执行的字节码的行数或者时间片来释放GIL锁,
在遇到IO操作时,也会释放GIL锁,所以并不是说只要线程一拿到锁,就直到执行完才释放,我们可以通过下面这个例子来说明。
import threading
total = 0
def add():
global total
for i in range(1000000):
total += 1
def desc():
global total
for i in range(1000000):
total -= 1
if __name__ == '__main__':
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(total)
上面这段代码的输出结果并不是0,这不是我们想要的结果,但也说明了线程在执行到一半的时候被切换掉了。
如果我们想保证数据的同步的话,我们就必须给其上锁。
我们可以通过导入锁来保证数据的同步,代码如下。
import threading
from threading import Lock
total = 0
lock = Lock()
def add():
global total
global lock
for i in range(1000000):
lock.acquire()
total += 1
lock.release()
def desc():
global total
global lock
for i in range(1000000):
lock.acquire()
total -= 1
lock.release()
if __name__ == '__main__':
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(total)
加了锁之后再运行,会发现结果是0
在python中,创建线程需要的模块是threading,如下图,这是第一种创建方式,args这个参数可以不要,但如果要写参数的话,必须是一个可迭代的对象。
import threading
def add(a):
a += 1
if __name__ == '__main__':
thread1 = threading.Thread(target=add,args=(0,))
thread1.start()
第二种创建线程的方式是通过继承线程类来创建进程。
import threading
class Add(threading.Thread):
def __init__(self,a):
super().__init__()
self.a = a
def run(self):
for i in range(1000000):
self.a += 1
if __name__ == '__main__':
ad = Add(0)
ad.start()
用这种方式创建进程的时候,需要重载run方法。