GIL全局解释器锁、死锁、递归锁、线程队列

GIL全局解释锁

  1. GIL本质上是一个互斥锁。

  2. GIL是为了阻止同一个进程内多个进程同时执行(并行)

    • 单个进程下的多个线程无法实现并行,但能实现并发
  3. 这把锁主要是因为Cpython的内存管理不是线程安全的

    • 保证线程在执行任务时不会被垃圾回收机制回收
from threading import Thread
import time

num = 100


def task():
    global num
    num2 = num
    time.sleep(1)
    num = num2 - 1
    print(num)


for line in range(100):
    t = Thread(target=task)
    t.start()
    
# 这里的运行结果都是99, 加了IO操作,所有线程都对num进行了减值操作,由于GIL锁的存在,没有修改成功,都是99

多线程的作用

  1. 计算密集型, 有四个任务,每个任务需要10s

单核:

  • 开启进程
    • 消耗资源过大
    • 4个进程: 40s
  • 开启线程
    • 消耗资源远小于进程
    • 4个线程: 40s

多核:

  • 开启进程
    • 并行执行, 效率比较高
    • 4个进程: 10s
  • 开启线程
    • 并发执行,执行效率低
    • 4个线程: 40s
  1. IO密集型, 四个任务, 每个任务需要10s

单核:

  • 开启进程
    • 消耗资源过大
    • 4个进程: 40s
  • 开启线程
    • 消耗资源远小于进程
    • 4个线程: 40s

多核:

  • 开启进程
    • 并行执行, 效率小于多线程, 但是遇到IO会立马切换CPU的执行权限
    • 4个进程: 40s + 开启进程消耗的额外时间
  • 开启线程
    • 并发执行,执行效率高于多进程
    • 4个线程: 40s

测试计算密集型

from threading import Thread
from multiprocessing import Process
import time
import os


# 计算密集型
def work1():
    number = 0
    for line in range(100000000):
        number += 1

# IO密集型
def work2():
    time.sleep(2)


if __name__ == '__main__':
    # 测试计算密集型
    print(os.cpu_count())   # 4核cpu

    start = time.time()
    list1 = []
    for line in range(6):

        p = Process(target=work1)  # 程序执行时间8.756593704223633
        # p = Thread(target=work1)   # 程序执行时间31.78555393218994
        list1.append(p)
        p.start()
    for p in list1:
        p.join()
    end = time.time()
    print(f'程序执行时间{end - start}')

IO密集型

from threading import Thread
from multiprocessing import Process
import time
import os

# 计算密集型
def work1():
    number = 0
    for line in range(100000000):
        number += 1

# IO密集型
def work2():
    time.sleep(1)


if __name__ == '__main__':
    # 测试计算密集型
    print(os.cpu_count())   # 4核cpu

    start = time.time()
    list1 = []
    for line in range(100):

        # p = Process(target=work2)  # 程序执行时间15.354223251342773
        p = Thread(target=work2)   # 程序执行时间1.0206732749938965
        list1.append(p)
        p.start()
    for p in list1:
        p.join()
    end = time.time()
    print(f'程序执行时间{end - start}')

结论:

  • 在计算密集型的情况下, 使用多进程
  • 在IO密集型的情况下, 使用多线程
  • 高效执行多个进程, 内有多个IO密集型程序,使用多进程 + 多线程

死锁现象

指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,如无外力作用,它们都无法推进下去.此时称系统处于死锁状态

以下就是死锁:

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

mutex_a = Lock()
mutex_b = Lock()


class MyThread(Thread):

    def run(self):
        self.func1()
        self.func2()

    def func1(self):
        mutex_a.acquire()
        print(f'用户{self.name}抢到锁a')
        mutex_b.acquire()
        print(f'用户{self.name}抢到锁b')
        mutex_b.release()
        print(f'用户{self.name}释放锁b')
        mutex_a.release()
        print(f'用户{self.name}释放锁a')

    def func2(self):
        mutex_b.acquire()
        print(f'用户{self.name}抢到锁b')
        time.sleep(1)
        mutex_a.acquire()
        print(f'用户{self.name}抢到锁a')
        mutex_a.release()
        print(f'用户{self.name}释放锁a')
        mutex_b.release()
        print(f'用户{self.name}释放锁b')


for line in range(10):
    t = MyThread()
    t.start()
    
    
'''
用户Thread-1抢到锁a
用户Thread-1抢到锁b
用户Thread-1释放锁b
用户Thread-1释放锁a
用户Thread-1抢到锁b
用户Thread-2抢到锁a
'''
# 一直等待

递归锁

用于解决死锁问题

RLock: 比喻成万能钥匙,可以提供给多个人使用

但是第一个使用的时候,会对该锁做一个引用计数

只有引用计数为0, 才能真正释放让一个人使用

上面的例子中用RLock代替Lock, 就不会发生死锁现象

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

# mutex_a = Lock()
# mutex_b = Lock()

mutex_a = mutex_b = RLock()

class MyThread(Thread):

    def run(self):
        self.func1()
        self.func2()

    def func1(self):
        mutex_a.acquire()
        print(f'用户{self.name}抢到锁a')
        mutex_b.acquire()
        print(f'用户{self.name}抢到锁b')
        mutex_b.release()
        print(f'用户{self.name}释放锁b')
        mutex_a.release()
        print(f'用户{self.name}释放锁a')

    def func2(self):
        mutex_b.acquire()
        print(f'用户{self.name}抢到锁b')
        time.sleep(1)
        mutex_a.acquire()
        print(f'用户{self.name}抢到锁a')
        mutex_a.release()
        print(f'用户{self.name}释放锁a')
        mutex_b.release()
        print(f'用户{self.name}释放锁b')


for line in range(10):
    t = MyThread()
    t.start()

信号量(了解)

互斥锁: 比喻成一个家用马桶, 同一时间只能让一个人去使用

信号比喻成公测多个马桶: 同一时间可以让多个人去使用

from threading import Semaphore
from threading import Thread
from threading import current_thread
import time

sm = Semaphore(5)

def task():
    sm.acquire()
    print(f'{current_thread().name}执行任务')
    time.sleep(1)
    sm.release()


for i in range(20):
    t = Thread(target=task)
    t.start()

线程队列

线程Q: 就是线程队列 FIFO

  • 普通队列: 先进先出 FIFO
  • 特殊队列: 后进先出 LIFO
  • 优先级队列: 若传入一个元组,会依次判断参数的ASCII的数值大小
import queue

# 普通的线程队列: 遵循先进先出
q = queue.Queue()
q.put(1)
q.put(2)
q.put(3)

print(q.get())  # 1
print(q.get())  # 2


# LIFO队列  后进先出
q = queue.LifoQueue()
q.put(1)
q.put(2)
q.put(3)
print(q.get())  # 3


# 优先级队列:根据参数内
q = queue.PriorityQueue()
q.put((4, '我'))
q.put((2, '你'))
q.put((3, 'ta'))
print(q.get())   # (2, '你')
posted @ 2019-10-23 21:57  SetCreed  阅读(279)  评论(0编辑  收藏  举报