多线程2

  多线程2

主要内容:

  • GIL全局解释器锁
  • 信号量
  • Event事件
  • 死锁
  • 递归锁
  • 队列

1.  GIL全局解释器锁

  GIL全称是global  interpreter lock, 中文译名就叫全局解释器锁. 

2.  信号量

  信号量在Python中的定义与普通的锁相对应,  普通的锁每次只能让一个线程获取, 而信号量(semaphore)则是可以同时拥有多把锁. 举个形象的例子来说,就是例如公共厕所, 它有5个位置, 当里面的位置被占满后, 外面的人就只能等里面有人出来了(释放锁)才能进去. 

from threading import Semaphore, Thread
import time


sem = Semaphore(4)


def task(name):
    with sem:
        print('线程{}进去'.format(name))
        time.sleep(1)
        print('线程{}出去'.format(name))


if __name__ == '__main__':
    for i in range(10):
        t = Thread(target=task, args=(i, ))
        t.start()

3.  Event事件

  event事件主要是用于两个线程之间的同步事件的, 例如一个线程需要根据另一个线程的状态来决定自己的下一步行动, 这时候就需要用到Event这个线程库的类了.

  以一个红绿灯的例子来看这个Event事件的使用方法. 

import time
from threading import Thread, Event

e = Event()


def traffic():
    """模拟交通灯"""
    while True:
        e.clear()   # 状态清空
        print('红灯停3s...')
        time.sleep(3)
        e.set()     # 设置为通行状态
        print('绿灯行3s')
        time.sleep(3)


def car(name):
    """模拟车辆需要不停的根据红绿灯来进行行进"""
    while True:
        if e.is_set():
            print('当前绿灯中, {}车一路飞速行进中...'.format(name))
            time.sleep(2)
        else:
            print('当前红灯中...{}车在等待...'.format(name))
            e.wait()


if __name__ == '__main__':
    t = Thread(target=traffic)
    t.start()

    c1 = Thread(target=car, args=('保时捷', ))
    c2 = Thread(target=car, args=('宝马', ))
    c3 = Thread(target=car, args=('奥迪', ))

    c1.start()
    c2.start()
    c3.start()

4.  死锁

  所谓死锁现象是指多个线程因为竞争资源而产生的一种相互等待的现象, 这一结果没有外力的作用下, 会永远进行下去. 通俗点来讲就是在两个线程中, 1线程手上拿着锁A,

但是还在等待着获取锁B, 但此时2线程却获取到了锁A, 而它也等待着获取锁B, 这种互相等待对方释放锁的现象就是死锁现象.

# -*- coding: UTF-8 -*-
import time
from threading import Thread, Lock

mutexA = Lock()
mutexB = Lock()


def task(name):
    with mutexA:
        print('线程{}抢到了锁A'.format(name))
        with mutexB:
            print('线程{}抢到了锁B'.format(name))
        print('线程{}释放锁B'.format(name))
    print('线程{}释放锁A'.format(name))

    with mutexB:
        print('线程{}抢到了锁B'.format(name))
        time.sleep(1)
        with mutexA:
            print('线程{}抢到了锁A'.format(name))
        print('线程{}释放了锁A'.format(name))
    print('线程{}释放了锁B'.format(name))


if __name__ == '__main__':
    t1 = Thread(target=task, args=('线程1号', ))
    t2 = Thread(target=task, args=('线程2号', ))
    t1.start()
    t2.start()

5.  递归锁

  递归锁的存在就是为了解决死锁现象的一种可重入锁, 这个锁内部内置了一个计数器, 一个线程可以重复获取这个锁,  然后每一次获取计数器都会加一, 其他线程在这个递归锁计数器归零之前都不能抢, 这就避免了死锁现象的产生. 

# -*- coding: UTF-8 -*-
import time
from threading import Thread, RLock

mutexA = mutexB = RLock()


def task(name):
    with mutexA:
        print('线程{}抢到了锁A'.format(name))
        with mutexB:
            print('线程{}抢到了锁B'.format(name))
        print('线程{}释放锁B'.format(name))
    print('线程{}释放锁A'.format(name))

    with mutexB:
        print('线程{}抢到了锁B'.format(name))
        time.sleep(1)
        with mutexA:
            print('线程{}抢到了锁A'.format(name))
        print('线程{}释放了锁A'.format(name))
    print('线程{}释放了锁B'.format(name))


if __name__ == '__main__':
    t1 = Thread(target=task, args=('线程1号', ))
    t2 = Thread(target=task, args=('线程2号', ))
    t1.start()
    t2.start()

  一行代码都没有改, 只是把锁换成了递归锁, 问题就随之解决了.

6.  队列

  在这个队列的模块中, 只要介绍了3种队列, 它们都有相同的接口,  最重要的方法是放数据 put方法,  和 取数据get方法, 其中get, put方法默认是阻塞的.

  1. Queue:

    这是一个普通的先进先出的队列, 即先放进去的元素, 会被先get取出来

  2. PriorityQueue:

    这是一个优先级队列, 即每次需要放进去一个由优先级和数据组成的元组, 优先级代表的数字越小, 那么优先级越高, 就会被先取出来.

  3: LifoQueue:

    这是一个后进先出的队列, 或是称之为栈的结构. 后放进去的数据, 会被先取出来

from queue import Queue, PriorityQueue, LifoQueue


q = Queue()
q.put(4)
q.put(3)
print(q.get())
print(q.get())

# out
# 4
# 3

q = PriorityQueue()
q.put((1, 100))
q.put((0, 300))
q.put((3, 200))
print(q.get())
print(q.get())
print(q.get())

# (0, 300)
# (1, 100)
# (3, 200)

q = LifoQueue()
q.put(1)
q.put(2)
print(q.get())
print(q.get())
# 2
# 1

 

posted @ 2019-08-15 06:00  yscl  阅读(78)  评论(0)    收藏  举报