python_并发编程——多线程2

1.互斥锁

import time
from threading import Thread,Lock

def func1(lock):
    global n
    lock.acquire()  #加锁
    temp = n
    time.sleep(0.2)
    n = temp -1
    lock.release()  #解锁

n = 10
t_list = []
lock = Lock()
for i in range(10):
    t1 = Thread(target=func1,args=(lock,))
    t1.start()
    t_list.append(t1)
for i in t_list:
    i.join()
print(n)

结果:  牺牲了执行的速度,但保证了数据的安全性。

 

 2.递归锁

from threading import Thread,RLock

def func1(name):
    lock1.acquire()
    print('{}拿到第一把钥匙!'.format(name))
    lock2.acquire()
    print('{}拿到第二把钥匙!'.format(name))
    print('{}进入了房间'.format(name))
    lock2.release() #归还第二把钥匙
    lock1.release() #归还第一把钥匙

lock1 = RLock()   #锁1
lock2 = RLock()   #锁2

for i in range(10):
    Thread(target=func1,args=(i,)).start()

结果:  当同时需要拿到两把钥匙才可以执行代码时,容易出现分别两个不同的线程,每个线程各拿到一把钥匙时,就会出现死锁,所有递归锁就可以解决这样的问题。

3.信号量

from threading import Semaphore,Thread
import time

def func(sem,a,b):
    sem.acquire()
    time.sleep(1)   #增加1秒之的间隔,更容易看出执行结果
    print(a+b)
    sem.release()

sem = Semaphore(4)  #定义一个信号量对象,设置好信号量的数量
for i in range(10):
    t = Thread(target=func,args=(sem,i,i+5))    #将信号来传递进子线程要执行的函数
    t.start()

结果:只能同时有4个进程访问函数。

4.事件

  当事件被创建出来的时候,他的状态是False,事件的状态可以控制wait(),当事件的状态为True时,wait()非阻塞,当事件状态为False时,wait()阻塞。

  有两个方法可以修改事件的状态,clear():设置事件的状态为False,set():设置事件的状态为True。

  应用:多线程模拟检测数据库链接状态:

import time
import random
from threading import Thread,Event

def connect_db(e):
    count = 0   #计数器
    while count < 3:
        e.wait(1)    #根据事件e的状态选择是否阻塞,括号里面加参数,设置阻塞时间1秒
        if e.is_set() == True:  #is_set():检测事件e的状态
            print('数据库链接成功!')
            break
        else:
            count += 1
            print('第{}次链接数据库失败!'.format(count))
    else:
        print('连接数据库超时!')

def check_web(e):
    time.sleep(random.randint(0,3))     #模拟网络延时0-3s
    e.set()     #将事件对象状态设置为True

e = Event() #创建一个事件对象
t1 = Thread(target=connect_db,args=(e,))
t2 = Thread(target=check_web,args=(e,))
t1.start()
t2.start()

结果:

5.条件

  条件类似一个高级的锁,他除了acquire()方法和release()方法之后还有两个方法,wait()方法和notify()方法。

一个条件被创建之初,默认会有一个状态,这个状态会影响wait()方法,一直处于等待状态。

notify(int数据类型)方法的括号中有一个int类型的数值,这个方法的作用是制作钥匙,括号中填写制作钥匙的数量。

wait和notify 都需要在acquire和release之间。

from threading import Thread,Condition

def func(con,i):
    con.acquire()
    con.wait()  #等待
    print('在第{}个函数中!'.format(i))
    con.release()

con = Condition()   #创建一个条件对象
for i in range(10):
    Thread(target=func,args=(con,i)).start()
while True:
    num = int(input('输入一个整数:'))
    con.acquire()
    con.notify(num)     #造钥匙
    con.release()

结果:  造多少个钥匙,就多少个线程访问函数。

6.定时器

from threading import Timer

def func():
    print('定时开启线程!')

#两个值 第一个:时间,以秒为单位,第二个:函数名
Timer(3,func).start()   #3秒周之后开启一个线程执行函数func

结果:3秒后,打印‘定时开启线程!

7.队列

  队列是安全的,队列中的数据先进先出。

import queue

q = queue.Queue(10)   # 创建一个队列对象,设置队列大小
for i in range(10):
    q.put(i)    # 向队列中存数据
for i in range(10):
    print(q.get())  # 从队列中获取数据

结果:

 

当队列中没有值时,还继续用get方法获取队列中的值,会一直等待,当队列中值满了的时候,还继续用put方法想队列中存放数据,也会一直阻塞。

put_nowait()方法:

import queue

q = queue.Queue(10)   # 创建一个队列对象,设置队列大小
for i in range(11):
    q.put(i)    # 向队列中存数据
    q.put_nowait()  # 当队列中值满了的时候,再继续向队列中存值,会报错

结果:

 

get_nowait()方法:

import queue

q = queue.Queue(10)
q.put('wdc')
print(q.get())
q.get_nowait()  # 当队列中没有值的时候,会报错

结果:

8.栈

  先进后出。

import queue

q = queue.LifoQueue(10)  # 创建一个栈对象,大小为10
for i in range(10):
    q.put(i)
for i in range(10):
    print(q.get())

结果:

9.优先级队列

import queue

q = queue.PriorityQueue()   # 创建一个优先级队列对象
q.put((20,'a'))  # 向优先级队列放数据时,要放一个元组,第一个代表优先级,第二个是要存放的数据
q.put((30,'b'))
q.put((10,'c'))
print(q.get())

结果:  先获取优先级数值小的元组。

import queue

q = queue.PriorityQueue()   # 创建一个优先级队列对象

q.put((10,'s'))
q.put((20,'a'))  # 向优先级队列放数据时,要放一个元组,第一个代表优先级,第二个是要存放的数据
q.put((30,'b'))
q.put((10,'c'))
q.put((10,'d'))
print(q.get())

结果: 当优先级相同时,会根据值的ascii编码的顺序获取。

posted @ 2019-12-29 18:57  手可摘星辰。  阅读(195)  评论(0编辑  收藏  举报