线程2

GIL

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple
native threads from executing Python bytecodes at once. This lock is necessary mainly
because CPython’s memory management is not thread-safe.

Python是一门解释器语言,代码想运行,必须通过解释器执行,Python存在多种解释器,分别基于不同语言开发,每个解释器有不同的特点,但都能正常运行Python代码,以下是常用的五种Python解释器:

CPython

当从Python官方网站下载并安装好Python2.7后,就直接获得了一个官方版本的解释器:Cpython,这个解释器是用C语言开发的,所以叫CPython,在命名行下运行python,就是启动CPython解释器,CPython是使用最广的Python解释器。

IPython

IPython是基于CPython之上的一个交互式解释器,也就是说,IPython只是在交互方式上有所增强,但是执行Python代码的功能和CPython是完全一样的,好比很多国产浏览器虽然外观不同,但内核其实是调用了IE。

PyPy

PyPy是另一个Python解释器,它的目标是执行速度,PyPy采用JIT技术,对Python代码进行动态编译,所以可以显著提高Python代码的执行速度。

Jython

Jython是运行在Java平台上的Python解释器,可以直接把Python代码编译成Java字节码执行。

IronPython

IronPython和Jython类似,只不过IronPython是运行在微软.Net平台上的Python解释器,可以直接把Python代码编译成.Net的字节码。

在Python的解释器中,使用广泛的是CPython,对于Python的编译,除了可以采用以上解释器进行编译外,技术高超的开发者还可以按照自己的需求自行编写Python解释器来执行Python代码,十分的方便!
GIL本质也是一把互斥锁,将并发变成串行牺牲效率保证数据的安全,用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行,但是可以实现并发)

在多线程环境中,Python 虚拟机按以下方式执行:

  a、设置 GIL;

  b、切换到一个线程去运行;

  c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));

  d、把线程设置为睡眠状态;

  e、解锁 GIL;

  d、再次重复以上所有步骤。
  在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。

GIL的存在是因为CPython解释器的内存管理不是线程安全的,就是多个线程访问同一个资源,会有问题,CPython中有一个互斥锁,防止线程同一时间执行python代码

之所以使用Cpython的原因??

C编译过的结果可以计算机直接识别

最主要的语言,C语言以后大量现成的,库(算法,通讯),Cpython可以无缝连接C语言的任何现成代码

 

垃圾回收机制
1.引用计数:内存中的数据如果没有任何的变量名与其有绑定关系,那么会被自动回收
2.标记清除:当内存快要被某个应用程序占满的时候,会自动触发
3.分代回收:根据值的存活时间的不同,划为不同的等级,等级越高垃圾回收机制扫描的频率越低

GIL是保证线程的安全,多个线程之间不会产生冲突,因为他同一时间只能执行一个任务

当垃圾回收启动后会将计数为0的数据清除掉,回收内存

分代回收

自动垃圾回收其实就是说,内部会有一个垃圾回收线程,会在某一时间运行起来,开始清理垃圾

这是可能会产生问题,例如线程1申请了内存,但是还没有使用CPU切换到了GC,GC将数据当成垃圾清理掉了

为了解决这个问题,Cpython就给解释器加上了互斥锁!

为什么Cpython要这么设计?

Cpython诞生于1991年 而多核处理器诞生2004年

当时不需要考虑多核效率问题

现在为什么不拿掉这个锁,因为这期间,很多已经完成的代码都依赖于这个锁,如果直接拿到,这些代码全得该,成本太大了

GIL锁的加锁与解锁时机

加锁: 只有有一个线程要使用解释器就立马枷锁

释放:

该线程任务结束

该线程遇到IO

该线程使用解释器过长 默认100纳秒

GIL给我们造成的影响

多线程不能并行

案例:

有一个下载任务 要从网络中下载一个文件 大小1G

和转换 任务 使用input 转为大写输出

上述任务并行执行,耗时也不会有太大的提升,反而开启多进程会浪费更多资源

这种任务称之为IO密集型,大量的时间都花在IO等待

这类问题使用多线程,

 

计算密集型任务

图像处理,语音处理,大数据分析,

区分任务类型

如果是IO密集使用多线程

如果是计算密集使用多进程

计算密集型
from multiprocessing import Process
from threading import Thread
import os,time

def work():
    res = 0
    for i in range(100000000):
        res*=i

if __name__ == '__main__':
    l = []
    print(os.cpu_count())
    start = time.time()
    for i in range(8):
        # p = Thread(target=work)
        p = Process(target=work)
        l.append(p)
        p.start()

    for p in l:
        p.join()

    stop = time.time()
    print('run time is %s' %(stop-start))


IO密集型
from multiprocessing import Process
from threading import Thread
import threading
import os,time
def work():
    time.sleep(2)

if __name__ == '__main__':
    l = []
    print(os.cpu_count())
    start = time.time()
    for i in range(4000):
        # p= Process(target=work)   #run time is 221.63026809692383
        p = Thread(target=work)  #run time is 2.3937768936157227
        l.append(p)
        p.start()

    for p in l:
        p.join()
    stop=time.time()
    print('run time is %s'%(stop-start))
View Code

GIL与普通的互斥锁

都是互斥锁

为什么有了GIL还需要自己加锁

GIL是加在解释器上的,只能锁住,解释器内部的资源,但是无法锁住我们自己开启资源

例如: 我们自己开启了一个json文件,多个线程要同时访问, 如果第一个线程写入数据写到一半,执行超时了,另一个线程过来接着写,就会产生问题,

自己开启共享资源还得自己所锁

像是申请内存 保存数据等等就不需要我们程序员考虑了 GIL已经搞定了

from threading import Thread
import time

n = 100

def task():
    global n
    tmp = n
    n = tmp - 1

t_list = []
for i in range(100):
    t = Thread(target=task)
    t.start()
    t_list.append(t)

for t in t_list:
    t.join()

print(n)

  

 死锁

可以被第一个抢到锁的人连续的acquire和release,每acquire一次锁身上的计数加1,每release一次锁身上的计数减1,只要锁的计数不为0,其他人都不能抢

from threading import Thread,Lock,current_thread,RLock
import time

mutexA = Lock()
mutexB = Lock()
# mutexA = mutexB = RLock()   #A B现在是同一把锁

class MyThread(Thread):
    def run(self):   #创建线程自动触发run方法,run方法内调用func1 func2相当于也是自动触发
        self.func1()
        self.func2()


    def func1(self):
        mutexA.acquire()
        print('%s抢到了A锁' % self.name)   #self.name等价于current_thread().name
        mutexB.acquire()
        print('%s抢到了B锁' %self.name)
        mutexB.release()
        print('%s释放了B锁' %self.name)
        mutexA.release()
        print('%s释放了A锁' %self.name)

    def func2(self):
        mutexB.acquire()
        print('%s抢到了B锁'%self.name)
        time.sleep(1)
        mutexA.acquire()
        print('%s抢到了A锁'%self.name)
        mutexA.release()
        print('%s释放了A锁'%self.name)
        mutexB.release()
        print('%s释放了B锁'%self.name)

for i in range(10):
    t = MyThread()
    t.start()
View Code

 

解锁
from threading import Thread,Lock,current_thread,RLock
import time

# mutexA = Lock()
# mutexB = Lock()
mutexA = mutexB = RLock()   #A B现在是同一把锁

class MyThread(Thread):
    def run(self):   #创建线程自动触发run方法,run方法内调用func1 func2相当于也是自动触发
        self.func1()
        self.func2()


    def func1(self):
        mutexA.acquire()
        print('%s抢到了A锁' % self.name)   #self.name等价于current_thread().name
        mutexB.acquire()
        print('%s抢到了B锁' %self.name)
        mutexB.release()
        print('%s释放了B锁' %self.name)
        mutexA.release()
        print('%s释放了A锁' %self.name)

    def func2(self):
        mutexB.acquire()
        print('%s抢到了B锁'%self.name)
        time.sleep(1)
        mutexA.acquire()
        print('%s抢到了A锁'%self.name)
        mutexA.release()
        print('%s释放了A锁'%self.name)
        mutexB.release()
        print('%s释放了B锁'%self.name)

for i in range(10):
    t = MyThread()
    t.start()
View Code

信号量

信号量可能在不同的领域中,对应不同的知识点

互斥锁:一个厕所(一个坑位)

信号量:公共厕所(多个坑位)

 

from threading import Semaphore,Thread
import time
import random
sm = Semaphore(5)   #造了一个含有五个的坑位的公共厕所


def task(name):
    sm.acquire()
    print('%s占了一个坑位'%name)
    time.sleep(random.randint(1,3))
    sm.release()

for i in range(40):
    t = Thread(target=task,args=(i,))
    t.start()

event事件

from threading import Event,Thread
import time

#先生成一个event对象
e = Event()

def light():
    print('红灯正亮着')
    time.sleep(3)
    e.set()   #发信号
    print('绿灯亮了')

def car(name):
    print('%s正在等红灯'%name)
    e.wait()   #等待信号
    print('%s加油门飙车了'%name)


t = Thread(target=light)
t.start()

for i in range(10):
    t = Thread(target=car,args=('伞兵%s'%i,))
    t.start()

线程b

import queue

q = queue.Queue()
q.put('hahaha')
print(q.get())

q = queue.LifoQueue()
q.put(1)
q.put(2)
q.put(3)
print(q.get())

q = queue.PriorityQueue()
#数字越小,优先级越高
q.put((10,'haha'))
q.put((100,'hehe'))
q.put((0,'xxx'))
q.put((-10,'yyy'))
print(q.get())

  

 

 

posted @ 2019-08-14 20:14  Aomur  阅读(163)  评论(0编辑  收藏  举报