04:全局解释器锁(GIL)

1 全局解释器锁(GIL)

 

 

0 pypy(没有全局解释器锁) cpython(99.999999%
   -pypy python好多模块用不了,
1 全局解释器锁,GIL锁(cpython解释器的问题)
-当年python设计的时候,还是单核,没有多核的概念
   -python需要做垃圾回收(gc)
   -垃圾回收线程,进行垃圾回收
   -设计了一个大锁(GIL锁),只有拿到这把锁的线程,才能执行
   -同一时刻,在一个进程中,可以开多个线程,但是只能有一条线程在执行
   -不能利用多核优势
 

只针对与cpython解释器(其他解释器,包括其他语言不这样)
2 如果是计算密集型:要开进程
3 如果是io密集型:要开线程

2 开启线程的两种方式

from threading import Thread
import time
​
#
# def task():
#     time.sleep(1)
#     print('我是子线程')
#
#
# if __name__ == '__main__':
#     t=Thread(target=task)
#     t.start()
#     print('我是主线程')
#
#
​
​
###第二种方式
class MyThread(Thread):
    def __init__(self,a):
        self.a=a
        super().__init__()
    def run(self):
        time.sleep(1)
        print('我是子线程',self.a)
​
if __name__ == '__main__':
    t=MyThread('aaaaa')
    t.start()
    print('我是主线程')

 

3 多线程与多进程比较

 

 

3.1 pid比较

3.2 开启速度比较

#开线程消耗的资源,耗费的时间远远小于开进程
from threading import Thread
import time
import os
from multiprocessing import Process
​
def task():
    time.sleep(0.1)
    print('我是子线程')
​
​
if __name__ == '__main__':
    ####线程
    # ctime=time.time()
    # t=Thread(target=task)
    # t.start()
    # t.join() # 等待子线程执行完成主线程再执行
    # print('我是主线程')
    # print(time.time()-ctime)
##进程
    ctime=time.time()
    t=Process(target=task)
    t.start()
    t.join() # 等待子线程执行完成主线程再执行
    print('我是主线程')
    print(time.time()-ctime)

 

3.3 内存数据的共享问题

##线程间数据共享
from threading import Thread
import time
import os
from multiprocessing import Process
​
def task():
    global n
    n=10
    print(n)
​
​
if __name__ == '__main__':
    ####线程
    n=100
    t=Thread(target=task)
    t.start()
    t.join() # 等待子线程执行完成主线程再执行
    print('我是主线程')
    print(n)
​

 


4 Thread类的其他方法


from threading import Thread
import threading
import time
def task():
    # time.sleep(0.01)
    #在子线程中执行
    # res = threading.currentThread()
    # print(res)
​
    res=threading.get_ident()
    print('子线程:',res)
    print('我是子线程')
​
​
if __name__ == '__main__':
    t=Thread(target=task)
    t1=Thread(target=task)
​
    t.start()
    t1.start()
    # print(t.is_alive())  #看线程是否存活
    #
    # print(t.getName() )  # 获取线程的名字
    # t.setName('lqz')   # 设置线程民资
    # print(t.getName() )
    #
    #
    # print('主线程')
    # time.sleep(0.02)
    # print(t.is_alive())
​
​
    # 主线程中执行,返回当前线程对象
    # res=threading.currentThread()
    # print(res)
​
​
    # 返回当前进程中正在运行的子线程对象列表
    # res=threading.enumerate()
    # print(res)
​
​
    # 返回当前正在运行的线程个数
    # res=threading.activeCount()
    # print(res)
# 线程id号
    res=threading.get_ident()
    print('主线程:',res)
    
    
    
    
   '''
   t.is_alive()
   t.getName()
   t.setName('lqz') 
   
   threading:模块下的一些方法
   res=threading.currentThread()
   res=threading.enumerate()
   res=threading.activeCount()
   res=threading.get_ident()
   '''

 

 

5 join方法

 

 

等待子线程执行结束

from threading import Thread
import time
​
def task():
    time.sleep(2)
    print('我是子线程')
​
​
if __name__ == '__main__':
    ll=[]
    for i in range(1000):
        t=Thread(target=task)
​
        t.start()
        ll.append(t)
​
    for i in ll:
        i.join()
​
​
​
    # 主线程等待子线程执行完再执行
    print('我是主线程,子线程全都执行完了')

 



6 守护线程

from threading import Thread
import time
​
def task():
    time.sleep(2)
    print('我是子线程')
​
​
if __name__ == '__main__':
    t=Thread(target=task)
    t.setDaemon(True)  # 如果主线程执行结束,子线程也结束(不执行了)
    t.start()
​
    #只要主线程执行结束,子线程也结束
    print('主线程执行结束')

 


7 同步锁(互斥锁)

 

 

## 多个线程操作同一个数据(变量),会出现并发安全的问题
# from threading import Thread,Lock
# import time
# import random
# def task():
#     global n
#
#
#     ### 临界区(加锁)
#     time.sleep(random.random())
#     temp=n
#     time.sleep(random.random())
#     temp=temp-1
#     n=temp
#
#     ##模拟不出来,因为太快了,没有cup的切换(io,时间片到了),模拟io,让cpu切换
#
#     # n-=1
#
#
# if __name__ == '__main__':
#     n=10
#     ll=[]
#     for i in range(10):
#         t=Thread(target=task)
#         t.start()
#         ll.append(t)
#
#     for i in ll:
#         i.join()
#
#
#     print(n)



###出现了并发安全的问题,加锁解决

from threading import Thread,Lock
import time
import random
def task_lock(lock):
    global n


    ### 临界区(加锁)
    with lock:
        time.sleep(random.random())
        temp=n
        time.sleep(random.random())
        temp=temp-1
        n=temp

    ##模拟不出来,因为太快了,没有cup的切换(io,时间片到了),模拟io,让cpu切换

    # n-=1

def task_no_lock():

    global n
    time.sleep(random.random())
    temp=n
    time.sleep(random.random())
    temp=temp-1
    n=temp



if __name__ == '__main__':
    n=10
    lock=Lock()
    ll=[]
    for i in range(10):
        # t=Thread(target=task_lock,args=[lock,])
        t=Thread(target=task_no_lock,args=[lock,])
        t.start()
        ll.append(t)
        t.join()

    # for i in ll:
    #     i.join()


    print(n)


'''
互斥锁和join的区别
如果使用互斥锁:只锁临界区,只有临界区是串行,其他地方还是并发的
如果使用join,整个过程全变成串行执行


'''

 

8 信号量

### 信号量可以理解为多把锁,同时允许多个线程来更改数据

from  threading import Thread,Semaphore

import time
import random
def task(sm,i):
    sm.acquire()
    print('%s:这个人在上厕所'%i)
    time.sleep(random.random())
    print('%s:这个人拉完了'%i)
    sm.release()



sm=Semaphore(5)
for i in range(40):
    t=Thread(target=task,args=[sm,i])
    t.start()

 

9 Event事件

 

 

Event事件:
一些线程需要等到其他线程执行完成之后才能执行,类似于发射信号
比如一个线程等待另一个线程执行结束再继续执行


# 一些线程需要等到其他线程执行完成之后才能执行,类似于发射信号
# 比如一个线程等待另一个线程执行结束再继续执行

from threading import Thread, Event

import time
import random


def girl(event):
    print('赵丽颖现在在结婚状态')
    time.sleep(1)
    # 离婚了,发送信号
    print('赵丽颖离婚了')
    event.set()  # 发送一个信号


def boy(i, event):
    print('屌丝%s:在等赵丽颖的离婚信号'%i)
    event.wait()  # 收不到信号之前,一直卡在这
    print('屌丝%s号,收到了离婚信号,开始追' % i)


event = Event()
t = Thread(target=girl, args=[event, ])
t.start()

for i in range(10):
    t1 = Thread(target=boy, args=[i, event])
    t1.start()



##  写两条线程,一条线程读一个文件的头2分之一,另一个线程读这个文件的后2分之一,但是必须第一个线程读完,发送信号后,第二个线程才能读

 

 

 

 

总结

1 GIL锁:全局解释器锁,在解释器之上的一把大锁,线程必须获得这把锁,才能执行,只针对与cpython解释器
2 GIL和线程锁有什么区别?有了GIL锁,为什么还要线程锁?
	-本身GIL和线程锁,都是线程级别的锁,GIL是内置的,解释器里的
    -线程锁:开发者定义的
3 多核cpu: 
	如果是计算密集型:开进程
    io密集型:开线程
    
4 开启线程的两种方式(对比进程)
5 进程和线程的比较
	-进程id比较
    -开启效率的比较
    -共享变量
    
6 Thread类的其他方法,threading模块下的其他方法

7 线程join(等待子线程执行完成)
8 守护线程(如果主线程执行完成,子线程也结束)
9 互斥锁,同步锁:为了保证并发情况下数据安全,把对数据的操作过程变成串行,牺牲了效率,保证了数据安全
10 信号量,Event
    
    



posted @ 2021-04-26 20:53  Jerry`  阅读(123)  评论(0编辑  收藏  举报