Python

运算调度的最小单位

线程

  1. 伪线程
    • Python没有做到真正实现线程的极致性能(受GIL锁的影响)
  2. 线程特点
    • 共享全局变量
  3. 应用场景
    • 运算率小的时候(共享数据的耗时操作,下载、爬虫、文件I/O等)
  4. GIL锁
    • 全局解释器锁(线程同步),优点:防止数据不安全,缺点:效率低
    • 原理是保证每个线程的任务做完时才让另一个线程接手(而不是做到一半阻塞了让另一个线程先做,导致从阻塞状态到运行状态时共享的数据变了)
    • 底层自动解开GIL锁的时机是达到一定运算率时(手动加锁不会自动解开)

代码示例:

import threading  # 导入线程类
from time import sleep


def foo(args):
    list01 = ['任务01', '任务02', '任务03']
    for i in list01:
        print('正在下载:{}'.format(i))
        sleep(args)  # 模拟阻塞状态
        print('下载 {} 成功!'.format(i))


if __name__ == '__main__':
    P01 = threading.Thread(target=foo, name='线程01', args=(1,))  # 创建线程对象
    P02 = threading.Thread(target=foo, name='线程02', args=(2,))
    P01.start()  # 启动线程
    P02.start()
    '''
    正在下载:任务01
    正在下载:任务01
    下载 任务01 成功!
    正在下载:任务02
    下载 任务01 成功!
    正在下载:任务02
    下载 任务02 成功!
    正在下载:任务03
    下载 任务03 成功!
    下载 任务02 成功!
    正在下载:任务03
    下载 任务03 成功!
    '''

多线程同步

代码示例:

import threading
import time

lock = threading.Lock()  # 获取锁

list01 = [0] * 2  # [0,0]


def foo():
    lock.acquire()  # 给任务加锁(可加上timeout参数)
    for i in range(len(list01)):
        list01[i] = 1
        time.sleep(0.3)
    lock.release()  # 给任务解锁


def bar():
    lock.acquire()
    for i in range(len(list01)):
        print(list01[i])
        time.sleep(0.3)
    lock.release()


if __name__ == '__main__':
    P01 = threading.Thread(target=foo)
    P02 = threading.Thread(target=bar)
    P01.start()
    P02.start()
    P01.join()
    P02.join()
    print(list01)
    '''
    1
    1
    1
    1
    1
    1
    1
    1
    1
    1
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    '''

死锁

  1. 概念
    • 如果两个线程分别占有一部分资源且同时等待对方占有的资源时,就会造成死锁
  2. 理解
    • 有两把锁(A,B)与两个线程(P01,P02)
    • P01拥有A锁,P01在执行的过程中阻塞了,执行权让给了P02
    • 而P02又拥有B锁,P02在执行的过程中也阻塞了,执行权让给了P01
    • P01在执行的过程中又想要B锁,但是B锁被P02占有了,只能无限等待P02释放锁(因为加锁本身是阻塞函数)
  3. 解决办法
    • 给acquire函数加个实参timeout(返回值:False)

代码示例:

from threading import Thread, Lock
from time import sleep

lock01 = Lock()
lock02 = Lock()


class Foo(Thread):
    def run(self):
        if lock01.acquire():  # 返回值:bool
            print(self.name + '获取了A锁')
            sleep(0.3)
            if lock02.acquire():
                print(self.name + '又获取了B锁,原来还有A锁')
                lock02.release()
            lock01.release()


class Bar(Thread):
    def run(self):
        if lock02.acquire():
            print(self.name + '获取了B锁')
            sleep(0.3)
            if lock01.acquire():
                print(self.name + '又获取了A锁,原来还有B锁')
                lock01.release()
            lock02.release()


if __name__ == '__main__':
    P01 = Foo()
    P02 = Bar()
    P01.start()
    P02.start()
    '''
    Thread-1获取了A锁
    Thread-2获取了B锁
    '''

线程间通信

代码示例:

import threading
import time
import random
import queue


def foo(args):
    for i in range(10):
        num = random.randint(1, 100)
        print(num)
        args.put(num)
        time.sleep(0.3)
    else:
        args.put(None)


def bar(args):
    while True:
        item = args.get()
        if item is None:
            break
        print('收到数据:{}'.format(item))
        time.sleep(0.3)


if __name__ == '__main__':
    P01 = queue.Queue(5)
    P02 = threading.Thread(target=foo, args=(P01,))
    P03 = threading.Thread(target=bar, args=(P01,))
    P02.start()
    P03.start()
    P02.join()
    P03.join()
    '''
    28
    收到数据:28
    51
    收到数据:51
    33
    收到数据:33
    53
    收到数据:53
    48
    收到数据:48
    81
    收到数据:81
    86
    收到数据:86
    1
    收到数据:1
    70
    收到数据:70
    67
    收到数据:67
    '''