GIL锁
Python虚拟机使用一个全局解释器锁(Global Interpreter Lock)来互斥线程对Python虚拟机的使用。为了支持多线程机制,一个基本的要求就是需要实现不同线程对共享资源访问的互斥,所以引入了GIL。
GIL:在一个线程拥有了解释器的访问权之后,其他的所有线程都必须等待它释放解释器的访问权,即使这些线程的下一条指令并不会互相影响。
在调用任何Python C API之前,要先获得GIL
GIL缺点:多处理器退化为单处理器;优点:避免大量的加锁解锁操作。
无论你启多少个线程,你有多少个cpu, Python在执行一个进程的时候会淡定的在同一时刻只允许一个线程运行。所以,python是无法利用多核CPU实现多线程的。
这样,python对于计算密集型的任务开多线程的效率甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的。
======================================================================
类的继承方式创建线程对象
# import threading,time
# class MyThread(threading.Thread):
# def __init__(self):
# super().__init__()
# def run(self):
# print('ok')
# time.sleep(2)
# print('t1 out')
#
# t1=MyThread()
# t1.start()
# print('ending')
同步锁:锁通常被用来实现对共享资源的同步访问。为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线程需等待其被释放),待资源访问完后,再调用release方法释放锁。
# import threading,time
# lock=threading.Lock()#实例化一个锁对象
# num=100
# def sub():
# global num
# lock.acquire()#获得一个锁
# timp=num
# time.sleep(0.1)
# num=timp-1
# lock.release()#释放一个锁
# time.sleep(2)
# l=[]
# for i in range(100):
# t=threading.Thread(target=sub,args=())
# t.start()
# l.append(t)
# for t in l:
# t.join()
#
#
# print(num)
'''
1、为什么有了GIL,还需要线程同步?
多线程环境下必须存在资源的竞争,那么如何才能保证同一时刻只有一个线程对共享资源进行存取?
加锁, 对, 加锁可以保证存取操作的唯一性, 从而保证同一时刻只有一个线程对共享数据存取.
通常加锁也有2种不同的粒度的锁:
coarse-grained(粗粒度): python解释器层面维护着一个全局的锁机制,用来保证线程安全。
内核级通过GIL实现的互斥保护了内核的共享资源。
fine-grained(细粒度): 那么程序员需要自行地加,解锁来保证线程安全,
用户级通过自行加锁保护的用户程序的共享资源。
2、GIL为什么限定在一个进程上?
你写一个py程序,运行起来本身就是一个进程,这个进程是有解释器来翻译的,所以GIL限定在当前进程;
如果又创建了一个子进程,那么两个进程是完全独立的,这个字进程也是有python解释器来运行的,所以
这个子进程上也是受GIL影响的
'''
可重入锁:在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。
import threading,time
# Rlock=threading.RLock()
# class MyThread(threading.Thread):
# def __init__(self):
# super().__init__()
# def run(self):
# self.foo()
# self.bar()
# def foo(self):
# Rlock.acquire()
# print('A {} {}'.format(self.name,time.ctime()))
# Rlock.acquire()
# print('B {} {}'.format(self.name,time.ctime()))
# Rlock.release()
# Rlock.release()
# def bar(self):
# Rlock.acquire()
# print('C {} {}'.format(self.name, time.ctime()))
# Rlock.acquire()
# print('D {} {}'.format(self.name, time.ctime()))
# Rlock.release()
# Rlock.release()
#
# for i in range(10):
# t=MyThread()
# t.start()
信号量
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
应用:连接池
import threading,time
s=threading.Semaphore(5)#最大可同时接入5个线程
def func():
if s.acquire():
print(threading.currentThread().getName())#当前线程的名字
time.sleep(1)
s.release()
for i in range(20):
t=threading.Thread(target=func,args=())
t.start()