python中的多线程 GIL(全局解释器锁) 死锁与递归锁

1.什么的是线程
在程序里一个执行路线就叫做线程,线程是程序执行的最小单位

2.多线程的优点

  • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
  • 在处理I/O密集程序的运行速度可能加快(ps:计算型密集任务除外)
  • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
  • 线程可以被抢占(中断)。
  • .在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) – 这就是线程的退让

3.多线程的缺点

  • 无法使用多核,由于gil锁的存在,他只能一个一个的取抢锁
  • 多线程对I/O 有效果,对于需要cpu计算的时候,反复切换会浪费时间反而会降低效率
  • 同时操作同一个共享资源,所以造成了资源破坏,即线程不安全

总结:

  1. Python 多线程并发,可以提高 i/o 密集型程序的运行效率,但对于计算密集型程序, Python

  2. 多线程并发并不能显著提升效率(并非是不适合使用,因为鬼知道现实世界会有什么样的应用场景)

4.python实现简单的多线程

Python3 通过两个标准库 _thread 和 threading 提供对线程的支持,由于_thread只是为了兼容python2的thread模块,所以推荐使用threading模块实现多线程。
threading提供了如下方法:

  • run(): 用以表示线程活动的方法。
  • start():启动线程活动。
  • join([time]): 等待到线程中止。
  • isAlive(): 返回线程是否活动的。
  • getName(): 返回线程名。
  • setName(): 设置线程名。
#导入线程模块
import threading
#导入时间模块
import time

#唱歌
def singing():
    for i in range(1,21):
        print("我会唱歌~~~ %d" %i)
        time.sleep(0.5)

#跳舞
def dancing():
    for i in range(1,21):
        print("我会跳舞^^^ %d" %i)
        time.sleep(0.5)

#打篮球
def play_basketball():
    for i in range(1,21):
        print("我还会打篮球··· %d" %i)
        time.sleep(0.5)

#练习
def practise():
    for i in range(1,21):
        print("我是来自美国,练习时长两年半的练习生$$$ %d" %i)
        time.sleep(0.5)

def main():
    """创建启动线程"""
    t_singing = threading.Thread(target=singing)
    t_dancing = threading.Thread(target=dancing)
    t_play_basketball = threading.Thread(target=play_basketball)
    t_practise = threading.Thread(target=practise)
    t_singing.start()
    t_dancing.start()
    t_play_basketball.start()
    t_practise.start()

if __name__ == '__main__':
    main()

结果:

我是来自美国,练习时长两年半的练习生$$$ 1
我会唱歌~~~ 2我会跳舞^^^ 2

我是来自美国,练习时长两年半的练习生$$$ 2
我还会打篮球··· 2
我会唱歌~~~ 3
我会跳舞^^^ 3
我还会打篮球··· 3
我是来自美国,练习时长两年半的练习生$$$ 3
我会唱歌~~~ 4
我会跳舞^^^ 4
我还会打篮球··· 4
我是来自美国,练习时长两年半的练习生$$$ 4
我会跳舞^^^ 5
我会唱歌~~~ 5
我是来自美国,练习时长两年半的练习生$$$ 5

5.GIL(Global Interpreter Lock)全局解释器锁

在非python环境中,单核情况下,同时只能有一个任务执行。多核时可以支持多个线程同时执行。但是在python中,无论有多少核,同时只能执行一个线程。究其原因,这就是由于GIL的存在导致的。

GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。GIL只在cpython中才有,因为cpython调用的是c语言的原生线程,所以他不能直接操作cpu,只能利用GIL保证同一时间只能有一个线程拿到数据。而在pypy和jpython中是没有GIL的。

6.Python多线程的工作过程:

python在使用多线程的时候,调用的是c语言的原生线程。

  • 拿到公共数据
  • 申请gil
  • python解释器调用os原生线程
  • os操作cpu执行运算
  • 当该线程执行时间到后,无论运算是否已经执行完,gil都被要求释放
  • 进而由其他进程重复上面的过程
  • 等其他进程执行完后,又会切换到之前的线程(从他记录的上下文继续执行),整个过程是每个线程执行自己的运算,当执行时间到就进行切换(context
    switch)。

简述:

线程锁的意义
当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁,即同一时刻允许一个线程执行操作。
也就是为了解决线程不安全问题

死锁和递归锁
什么是死锁:就是两个线程都等待对方释放资源。

案例:

#死锁
from threading import Thread,Lock
import time
mutexA = Lock()
mutexB = Lock()


class Work(Thread):
    def run(self):
        self.f1()
        self.f2()

    def f1(self):
        mutexA.acquire()
        print('%s 拿到了A锁 ' % self.name)
        mutexB.acquire()
        print('%s拿到了B锁' % self.name)
        mutexB.release()
        print('%s 释放了 B锁' % self.name)
        mutexA.release()
        print('%s 释放了 A锁' % self.name)

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

if __name__ == '__main__':
    for i in range(5):
        t = Work()
        t.start()

结果:
Thread-1 拿到了A锁
Thread-1拿到了B锁
Thread-1 释放了 B锁
Thread-1 释放了 A锁
Thread-2 拿到了A锁
Thread-1 拿到了B锁

递归锁
递归锁:Rlock

Rlock内部有一个count 初始为0 ,锁一下加1,释放了就减1

 #递归锁
from threading import Thread,RLock
import time
mutexA = mutexB = RLock()
class Work(Thread):
    def run(self):
        self.f1()
        self.f2()

    def f1(self):
        mutexA.acquire()
        print('%s 拿到了A锁 ' % self.name)
        mutexB.acquire()
        print('%s拿到了B锁' % self.name)
        mutexB.release()
        print('%s 释放了 b锁' % self.name)
        mutexA.release()
        print('%s 释放了 a锁' % self.name)

    def f2(self):
        mutexB.acquire()
        time.sleep(2)
        print('%s 拿到了A锁 ' % self.name)
        mutexA.acquire()
        print('%s拿到了B锁' % self.name)
        mutexA.release()
        print('%s 释放了 b锁' % self.name)
        mutexB.release()
        print('%s 释放了 a锁' % self.name)

if __name__ == '__main__':
    for i in range(3):
        t = Work()
        t.start()

结果:

Thread-1 拿到了A锁 
Thread-1拿到了B锁
Thread-1 释放了 b锁
Thread-1 释放了 a锁
Thread-1 拿到了A锁 
Thread-1拿到了B锁
Thread-1 释放了 b锁
Thread-1 释放了 a锁
Thread-2 拿到了A锁 
Thread-2拿到了B锁
Thread-2 释放了 b锁
Thread-2 释放了 a锁
Thread-2 拿到了A锁 
Thread-2拿到了B锁
Thread-2 释放了 b锁
Thread-2 释放了 a锁
Thread-3 拿到了A锁 
Thread-3拿到了B锁
Thread-3 释放了 b锁
Thread-3 释放了 a锁
Thread-3 拿到了A锁 
Thread-3拿到了B锁
Thread-3 释放了 b锁
Thread-3 释放了 a锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值