Python3 并发编程3

Posted MrBigB

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python3 并发编程3相关的知识,希望对你有一定的参考价值。

GIL全局解释器锁

基本概念

  • global interpreter lock 全局解释器锁
  • GIL不是Python的特性, 是Cpython解释器的特性
  • GIL本质是一个互斥锁
  • 原因: Cpython解释器的内存管理不是线程安全的
  • 作用: 保证同一时间一个线程内只有一个线程在执行

多线程的作用

  • 计算密集型---多进程, GIL原因, 一个进程内的线程只能并发, 不能并行
  • I/O密集型---多线程, 开启线程与切换线程的速度要快于进程
# 计算密集型
import time
import os
from multiprocessing import Process
from threading import Thread


# 计算密集型
def task1():
    number = 0
    for i in range(100000000):
        number += 1
    print('done!')

if __name__ == '__main__':
    start_time = time.time()
    lis = []
    for i in range(4):
        # p = Process(target=task1)  # 程序执行时间为16.711955785751343
        t = Thread(target=task1)  # 程序执行时间为26.467514038085938
        lis.append(t)
        t.start()

    for t in lis:
        t.join()

    end_time = time.time()
    print(f'程序执行时间为{end_time - start_time}')
# I/O密集型
import time
import os
from multiprocessing import Process
from threading import Thread


# I/O密集型
def task2():
    time.sleep(1)


if __name__ == '__main__':
    start_time = time.time()
    lis = []
    for i in range(20):
        # p = Process(target=task2)  # 程序执行时间为5.277301788330078
        t = Thread(target=task2)  # 程序执行时间为1.0040574073791504
        lis.append(t)
        t.start()

    for t in lis:
        t.join()

    end_time = time.time()
    print(f'程序执行时间为{end_time - start_time}')

死锁现象

  • 两个或者两个以上的线程在执行过程中, 因为争夺资源而产生的相互等待的状况
from threading import Thread, Lock
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')
        # I/O操作
        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')


if __name__ == '__main__':
    for i in range(4):
        t = MyThread()
        t.start()
        
        
'''
Thread-1拿到了锁a
Thread-1拿到了锁b
Thread-1释放了锁b
Thread-1释放了锁a
Thread-1拿到了锁b
Thread-2拿到了锁a
'''

递归锁

  • RLock 内部维护一个Lock和一个计数的counter, counter记录了acquire次数, 使得资源可以被多次请求
  • 直到一个线程所有的acquire都被release, 其他线程才能获取资源
from threading import Thread, RLock
import time

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')
        # I/O操作
        time.sleep(3)

        mutex_a.acquire()
        print(f'{self.name}拿到了锁a')
        mutex_a.release()
        print(f'{self.name}释放了锁a')
        mutex_b.release()
        print(f'{self.name}释放了锁b')


if __name__ == '__main__':
    for i in range(4):
        t = MyThread()
        t.start()

'''
Thread-1拿到了锁a
Thread-1拿到了锁b
Thread-1释放了锁b
Thread-1释放了锁a
Thread-1拿到了锁b
---间隔了3秒---
Thread-1拿到了锁a
Thread-1释放了锁a
Thread-1释放了锁b
Thread-2拿到了锁a
Thread-2拿到了锁b
Thread-2释放了锁b
Thread-2释放了锁a
Thread-2拿到了锁b

---间隔了3秒---

Thread-2拿到了锁a
Thread-2释放了锁a
Thread-2释放了锁b
Thread-4拿到了锁a
Thread-4拿到了锁b
Thread-4释放了锁b
Thread-4释放了锁a
Thread-4拿到了锁b

---间隔了3秒---

Thread-4拿到了锁a
Thread-4释放了锁a
Thread-4释放了锁b
Thread-3拿到了锁a
Thread-3拿到了锁b
Thread-3释放了锁b
Thread-3释放了锁a
Thread-3拿到了锁b
Thread-3拿到了锁a
Thread-3释放了锁a
Thread-3释放了锁b
'''

信号量

  • from threading import Semaphore
  • 相当于多个互斥锁, 可以控制多个线程来访问数据 (可以控制访问资源的线程数量)
  • sm = Semaphore(5) 表示一次允许5个线程访问数据
  • acquire 一次, 括号内数字减一, release一次加一, 为0时限制其他线程访问
from threading import Thread, Semaphore, current_thread
import time

# 一次允许5个线程访问数据
sm = Semaphore(5)


def task():
    sm.acquire()
    print(f'{current_thread().name}已运行...')
    time.sleep(3)
    sm.release()


if __name__ == '__main__':
    for i in range(20):
        t = Thread(target=task)
        t.start()
        
        
'''
Thread-1已运行...
Thread-2已运行...
Thread-3已运行...
Thread-4已运行...
Thread-5已运行...

---间隔了3秒---

Thread-6已运行...
Thread-7已运行...
Thread-8已运行...
Thread-9已运行...
Thread-10已运行...

--间隔了3秒---

Thread-11已运行...
Thread-12已运行...
Thread-13已运行...
Thread-14已运行...
Thread-15已运行...

---间隔3秒---

Thread-17已运行...
Thread-16已运行...
Thread-18已运行...
Thread-19已运行...
Thread-20已运行...
'''

线程队列

  • queue.Queue()FIFO 先进先出
  • queque.LifoQueue() LIFO 后进先出
  • queque.PriorityQueue() 优先级, 根据元祖内的数据排序
import queue

# 先进先出 FIFO
q1 = queue.Queue()
q1.put(1)
q1.put(2)
q1.put(3)

print(q1.get())  # 1

# 后进先出 LILO
q2 = queue.LifoQueue()
q2.put(1)
q2.put(2)
q2.put(3)
print(q2.get())  # 3

# 优先级 按元祖内的数据排序
q3 = queue.PriorityQueue()
q3.put(('a',))
q3.put(('b',))
q3.put(('c',))
print(q3.get())  # ('a',)

以上是关于Python3 并发编程3的主要内容,如果未能解决你的问题,请参考以下文章

Python3 与 C# 并发编程之~ 上篇

asyncio--python3未来并发编程主流充满野心的模块

Python3 与 C# 并发编程之~ 进程篇

学习笔记:python3,代码片段(2017)

JUC并发编程 共享模式之工具 JUC CountdownLatch(倒计时锁) -- CountdownLatch应用(等待多个线程准备完毕( 可以覆盖上次的打印内)等待多个远程调用结束)(代码片段

golang代码片段(摘抄)