为了防止读取到脏数据,对临界资源进行加锁,将并行被迫改为串行。通过threading.Lock()方法创建一把锁。
acquire() 方法:只有一个线程能成功的获取锁,按先后顺序 其他线程只能等待。release() 方法:线程释放。这把锁不允许在同一线程中被多次acquire()。
import threading
import time
def check():
global number
lock.acquire() # 对临界资源进行加锁
temp = number
time.sleep(0.01)
number = temp-1
lock.release()
l = []
number = 100
# 实例一把锁
lock = threading.Lock()
for i in range(100):
t = threading.Thread(target=check)
t.start()
l.append(t)
for t in l:
t.join()
print(number)
递归锁: RLock允许在同一线程中被多次acquire() 加锁,其内部存在着一个计数器,只要计数器大于0就没人能拿到这把锁。
acquire()方法和release()方法必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的锁。
通过 rlock = threading.Rlock() 方法获得一把递归锁。
import time
import threading
class MyThread(threading.Thread):
def run(self):
self.actionA()
self.actionB()
def actionA(self):
LockA.acquire()
print("got Alock"+str(threading.current_thread()))
time.sleep(1)
LockB.acquire()
print("got Block" + str(threading.current_thread()))
time.sleep(0.)
LockB.release()
LockA.release()
def actionB(self):
LockB.acquire()
print("got Block"+str(threading.current_thread()))
time.sleep(1)
LockA.acquire()
print("got Alock" + str(threading.current_thread()))
time.sleep(0.1)
LockA.release()
LockB.release()
if __name__ == '__main__':
LockA = threading.Lock()
LockB = threading.Lock()
L = []
for i in range(5):
t = MyThread()
t.start()
L.append(t)
for t in L:
t.join()
print('ending')
当一个线程执行完actionA方法以后,会继续执行actionB方法,此时并不会产生死锁。当第二个线程开始执行的时候,可以获得A锁,当获取B锁的时候,返现B锁已经被第一个线程占用,而第一个线程也在请求A锁。两个线程僵持不下所以发生死锁。当加上递归锁以后就解决此问题。
加上递归锁:
import time
import threading
class MyThread(threading.Thread):
def run(self):
self.actionA()
self.actionB()
def actionA(self):
LockA.acquire()
print("got Alock"+str(threading.current_thread()))
time.sleep(1)
LockA.acquire()
print("got Block" + str(threading.current_thread()))
time.sleep(0.)
LockA.release()
LockA.release()
def actionB(self):
LockA.acquire()
print("got Block"+str(threading.current_thread()))
time.sleep(1)
LockA.acquire()
print("got Alock" + str(threading.current_thread()))
time.sleep(0.1)
LockA.release()
LockA.release()
if __name__ == '__main__':
# 获得一把锁
LockA = threading.RLock()
L = []
# 实例线程对象
for i in range(5):
t = MyThread()
t.start()
L.append(t)
for t in L:
t.join()
print('ending')
当第一个线程执行完actionA方法以后再执行actionB方法会再次请求一把相同的锁,和其他线程一同竞争获得该锁。从而解决了此问题。
保证两个或多个关键代码段不被并发调用,也是一种锁。threading.Semaphore(size) 创建一个信号量。也有acquire()、release()方法和上面相同。
import threading
import time
def check():
# 当进来一个变量,就会减一,一共有5把锁
if semaphore.acquire():
print(threading.current_thread())
time.sleep(3)
# 每释放一把锁就会加一
semaphore.release()
# 最后显示会每三秒出5个结果
if __name__ == '__main__':
L = []
# 创建5个信号量,信号量也是一种锁
semaphore = threading.Semaphore(5)
for i in range(100):
t = threading.Thread(target=check)
t.start()
L.append(t)
for t in L:
t.join()
print('end')
threading.event()创建一个同步对象,类似于信号量机制,只不过信号量为一。
wait()方法:没有设定标志位就会卡住,一旦被设定等同于pass。set()方法:设定标志位。clear()方法:清楚标志位。is_set()方法:是否设置。
import time
import threading
class Boss(threading.Thread):
def run(self):
print("加班加班")
# 检验是否设置标志位
print(event.is_set())
# 设置标志位
event.set()
time.sleep(3)
# 清空标志位
event.clear()
print('加班结束')
event.set()
class Worker(threading.Thread):
def run(self):
# 等待设置标志位,如果设置,此语句相当于pass
event.wait()
print("命苦啊")
time.sleep(1)
event.clear()
event.wait()
print("OH yeah")
if __name__ == '__main__':
L = []
# 创建一个同步对象
event = threading.Event()
for i in range(5):
L.append(Worker())
L.append(Boss())
for t in L:
t.start()
for t in L:
t.join()
print("end")
(1):队列:线程队列一种数据结构,是多线程的、安全的。列表是线程不安全的。
队列的模式:1、先进先出 2、先进后出 3、按优先级。
import queue
# 创建一个队列先进先出
q = queue.Queue(3)
# q = queue.LifoQueue() 创建先进后出队列。(栈)later in first out
# q = queue.PriorityQueue() 创建一个按优先级顺序的队列
# 添加数据 当超过队列大小时,就会卡住put。后面如果加上false参数就会在满的时候报错。
q.put(1)
q.put("csdn")
q.put({"name":"2333"})
q.put("baba")
# print(q.qsize()) 不是最大值,而是当前队列有多少个元素。
# print(q.maxsize) 队列的最长度
# print(q.full()) 队满
# print(q.empty()) 队空
while 1:
print("+++++++++")
# 取数据,没数据的时候会一直卡住,一直等到有添加值。(另外的一个线程可以继续添加值)。后面如果加上false参数就会在满的时候报错
print(q.get())
print("---------")
当前程序会卡在put()方法。
当使用优先级队列时在put()数据时一同写上优先级,数值越小优先级越高。
q.put([1,1])
(2)Producer-consumer problem:
task_done()方法:完成一项工作后向另一个线程发信号。join()方法:等到队列为空时,在执行别的操作。成对出现,否则没意义。
import time, threading, queue, random
def producer(name):
count = 0
while count < 10:
print("making....")
# 随机数范围1--2
time.sleep(random.randrange(3))
q.put(count)
print("{}做好了第{}个包子".format(name,count))
count += 1
# q.task_done()
q.join()
def consumer(name):
count = 0
while count < 10:
time.sleep(random.randrange(4))
print('waiting')
# q.join() 没有信号会卡住,有人给信号就往下执行
data = q.get()
q.task_done()
print("{}正在吃第{}个包子".format(name,data))
count += 1
if __name__ == '__main__':
q = queue.Queue()
t1 = threading.Thread(target=producer,args=('A君',))
t2 = threading.Thread(target=consumer,args=('B君',))
t3 = threading.Thread(target=consumer,args=('C君',))
t4 = threading.Thread(target=consumer,args=('D君',))
t1.start()
t2.start()
t3.start()
t4.start()
task_done : 用于 get 的后续调用.告诉队列任务处理完成.当然你也可以不调用get。
join:阻塞操作,直到队列所有的任务都处理,换句话说,当队列里面没有东西的时候才会向下执行,否则会阻塞。也就是说:往里 put 几次,就要调用task_done几次。然而task_done()并不会使队列长度-1,而是会向队列发送信号,真正使队列长度减一的是get()操作。
put队列完成的时候千万不能用task_done(),否则会报错,因为该方法仅仅表示get成功后,执行的一个标记。