day 32-2 网络编程——认识线程(下)

死锁

所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁。

from threading import Thread, Lock
import time


class MyRLock(Thread):
    mexty = Lock()
    mexty2 = Lock()

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

    def func(self):
        self.mexty.acquire()
        print('%s 拿到 A 锁' % self.name)

        self.mexty2.acquire()
        print('%s 拿到 B 锁' % self.name)
        self.mexty2.release()

        self.mexty.release()

    def func2(self):
        self.mexty2.acquire()
        print('%s 拿到 B 锁' % self.name)
        time.sleep(0.5)

        self.mexty.acquire()
        print('%s 拿到 A 锁' % self.name)
        self.mexty.release()

        self.mexty2.release()


if __name__ == '__main__':
    for i in range(10):
        t = MyRLock()
        t.start()

执行结果:出现死锁,整个程序阻塞住

# Thread-1 拿到 A 锁
# Thread-1 拿到 B 锁
# Thread-1 拿到 B 锁
# Thread-2 拿到 A 锁

递归锁

解决方法,递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。

这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁,二者的区别是:递归锁可以连续acquire多次,而互斥锁只能acquire一次。

递归锁可以连续 acquire 多次,每 acquire 一次计数器则 +1,只有计数为 0 时,才能被抢到 acquire。

from threading import Thread, RLock
import time


class MyRLock(Thread):
    mexty = mexty2 = RLock()

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

    def func(self):
        self.mexty.acquire()
        print('%s 拿到 A 锁' % self.name)

        self.mexty2.acquire()
        print('%s 拿到 B 锁' % self.name)
        self.mexty2.release()

        self.mexty.release()

    def func2(self):
        self.mexty2.acquire()
        print('%s 拿到 B 锁' % self.name)
        time.sleep(0.5)

        self.mexty.acquire()
        print('%s 拿到 A 锁' % self.name)
        self.mexty.release()

        self.mexty2.release()


if __name__ == '__main__':
    for i in range(10):
        t = MyRLock()
        t.start()

信号量

信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行,信号量同一时间可以有5个任务拿到锁去执行,如果说互斥锁是合租房屋的人去抢一个厕所,那么信号量就相当于一群路人争抢公共厕所,公共厕所有多个坑位,这意味着同一时间可以有多个人上公共厕所,但公共厕所容纳的人数是一定的,这便是信号量的大小。

解析

# Semaphore管理一个内置的计数器,
# 每当调用acquire()时内置计数器-1;
# 调用release() 时内置计数器+1;
# 计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
from threading import Thread, Semaphore, currentThread
import time
import random


sm = Semaphore(3)
def func():
    with sm:
        print('%s 正在座位上' % currentThread().getName())
        time.sleep(random.randint(1, 3))


if __name__ == '__main__':
    for i in range(10):
        s = Thread(target=func,)
        s.start()

Event事件

线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行

# from threading import Event

# event.isSet():返回event的状态值;

# event.wait():如果 event.isSet()==False将阻塞线程;

# event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;

# event.clear():恢复event的状态值为False。

 E.wait() 可以设置等待时间,即超过此时间为超时,不在等待。

from threading import Thread, Event
import time

E = Event()


def student(name):
    print('%s 学生正在上课' % name)
    E.wait()
    # E.wait(1)
    print('%s 学生下课' % name)


def teacher(name):
    print('%s 老师正在上课' % name)
    time.sleep(3)
    print('%s 老师正在下课' % name)
    E.set()


if __name__ == '__main__':
    s = Thread(target=student, args=('stu',))
    s2 = Thread(target=student, args=('stu2',))
    s3 = Thread(target=student, args=('stu3',))

    t = Thread(target=teacher, args=('ysg',))

    lis = [s, s2, s3, t]
    for i in lis:
        i.start()

执行结果:

# E.wait()
# stu 学生正在上课
# stu2 学生正在上课
# stu3 学生正在上课
# ysg 老师正在上课
# ysg 老师正在下课
# stu2 学生下课
# stu3 学生下课
# stu 学生下课

# E.wait(1)
# stu 学生正在上课
# stu2 学生正在上课
# stu3 学生正在上课
# ysg 老师正在上课
# stu 学生下课
# stu2 学生下课
# stu3 学生下课
# ysg 老师正在下课

模拟多个客户端连接服务端,限制连接次数

from threading import Thread, Event, currentThread
import time

E = Event()


def client():
    n = 0
    while not E.is_set():
        print('%s 正在连接' % currentThread().getName())
        E.wait(0.5)
        n += 1
        if n == 3:
            print('重试次数超限')
            return
    print('连接成功')


def check():  # 检测服务端是否运行
    print('%s 正在检测' % currentThread().getName())
    time.sleep(5)
    E.set()
    print('检测完成')


if __name__ == '__main__':
    for i in range(3):
        c = Thread(target=client, )
        c.start()

定时器

定时器,指定n秒后执行某操作

from threading import Timer


def func(name):
    print('hello %s' % name)


if __name__ == '__main__':
    t = Timer(2, func, args=('ysg',))
    t.start()

验证码小例子

from threading import Timer
import random


class Timers:
    def __init__(self):
        super().__init__()
        self.myTimer()

    def myTimer(self):
        self.info = self.make_timer()
        print(self.info)
        self.t = Timer(5, self.myTimer)
        self.t.start()

    def make_timer(self, num=6):
        lis = ''
        for i in range(num):
            s1 = str(random.randint(0, 9))
            s2 = chr(random.randint(65, 90))
            lis += random.choice([s1, s2])
        return lis

    def run(self):
        while True:
            res = input('>>>').strip()
            if res == self.info:
                print('验证成功')
                self.t.cancel()
                break

t = Timers()
t.run()

线程queue

queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.

有三种不同的用法

class queue.Queue(maxsize=0) # 队列:先进先出

import queue

q = queue.Queue(3)
q.put('hello')
q.put(123)
q.put([1, 2, 3])
# q.put(456,block=False)  # 队列长度为 3,再继续放,报错:queue.Full

print(q.get())
print(q.get())
print(q.get())
# print(q.get(block=False))  # 队列中数据已全部取走,再继续取,报错:queue.Empty

# 输出结果 先进先出
# hello
# 123
# [1, 2, 3]

class queue.LifoQueue(maxsize=0) #堆栈:last in fisrt out

注意:用法与队列一样

import queue

q = queue.LifoQueue(3)
q.put('hello')
q.put(123)
q.put([1, 2, 3])

print(q.get())
print(q.get())
print(q.get())

# 输出结果 先进先出
# [1, 2, 3]
# 123
# hello

class queue.PriorityQueue(maxsize=0) #优先级队列:存储数据时可设置优先级的队列

import queue

q = queue.PriorityQueue(3)
q.put((10, 'hello'))
q.put((50, 123))
q.put((45, [1, 2, 3]))

print(q.get())
print(q.get())
print(q.get())


# 执行结果  按照优先级取出
# (10, 'hello')
# (45, [1, 2, 3])
# (50, 123)

猜你喜欢

转载自www.cnblogs.com/ysging/p/12339912.html