1、同步锁 (Lock)

  当全局资源(counter)被抢占的情况,问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。这种现象称为“线程不安全”。在开发过程中我们必须要避免这种情况,那怎么避免?这就用到了互斥锁了。

例如:

 import threading,time
def sub():
global num #对全局变量进行操作 temp=num
time.sleep(0.001) #模拟线程执行中出现I/o延迟等
num=temp- #所有线程对全局变量进行减一 time.sleep() num=
l=[] for i in range():
t=threading.Thread(target=sub,args=())
t.start()
l.append(t) for obj in l:
obj.join() print(num) #执行结果不可预期:
>>:
>>:
>>:

互斥锁概念

  Python编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为” 互斥锁” 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。在Python中我们使用threading模块提供的Lock类。

  我们对上面的程序进行整改,为此我们需要添加一个互斥锁变量lock = threading.Lock(),然后在争夺资源的时候之前我们会先抢占这把锁lock.acquire(),对资源使用完成之后我们在释放这把锁mutex.release()。

代码如下:

import threading,time
def sub():
global num lock.acquire()
temp=num
time.sleep(0.01)
num=temp-1
lock.release() time.sleep(1) num=100
l=[]
lock=threading.Lock()
for i in range(100):
t=threading.Thread(target=sub,args=())
t.start()
l.append(t) for obj in l:
obj.join() print(num)

2、死锁与递归锁

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

会产生死锁的例子:

class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self) def run(self):
self.foo() def foo(self):
LockA.acquire()
print('I am %s GET LOCKA---------%s'%(self.name,time.ctime()))
LockB.acquire()
print('I am %s GET LOCKB---------%s' % (self.name, time.ctime())) LockB.release() LockA.release() LockA=threading.Lock()
LockB=threading.Lock() for i in range(10):
t=MyThread()
t.start()

使用递归锁解决:

  在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:

class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self) def run(self):
self.foo()
self.bar() def foo(self):
RLock.acquire()
print('I am %s GET LOCKA---------%s'%(self.name,time.ctime()))
RLock.acquire()
print('I am %s GET LOCKB---------%s' % (self.name, time.ctime())) RLock.release()
RLock.release() def bar(self): RLock.acquire()
print('I am %s GET LOCKB---------%s' % (self.name, time.ctime()))
time.sleep(1)
RLock.acquire()
print('I am %s GET LOCKA---------%s' % (self.name, time.ctime())) RLock.release()
RLock.release() RLock=threading.RLock() for i in range(10):
t=MyThread()
t.start()

  

3、Semaphore(信号量)

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

实例:(同时只有5个线程可以获得semaphore,即可以限制最大连接数为5):

 import threading
import time semaphore = threading.Semaphore() def func():
if semaphore.acquire():
print (threading.currentThread().getName() + ' get semaphore')
time.sleep()
semaphore.release() for i in range():
t1 = threading.Thread(target=func)
t1.start()

4、Event对象

  线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就 会变得非常棘手。

  为了解决这些问题,我们需要使用threading库中的Event对象。Event对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。

  在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行

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

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

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

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

用evnt对象模拟红绿灯:

import queue,threading,time
import random event = threading.Event()
def light():
while True:
event.set()
for i in range(10):
print('light green')
time.sleep(1)
event.clear()
for i in range(10,13):
print('light yellow')
time.sleep(1)
for i in range(13,21):
print('light red')
time.sleep(1) def car(i):
while True:
time.sleep(random.randint(1,5))
if event.isSet():
print('car %s is runing'%i)
else:
print('car %s is waiting'%i) if __name__ == '__main__':
l1=threading.Thread(target=light)
l1.start() for i in range(5):
i = threading.Thread(target=car,args=(i,))
i.start()

4、队列(queue)

'''

创建一个“队列”对象

import queueq
q = queue.Queue(maxsize = 10)
    #queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数
    #maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。

q.put()    将一个值22放入队列中

    #调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。

q.get()    将一个值从队列中取出    

    #调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。

'''

queue的常用方法

'''

此包中的常用方法(q = Queue.Queue()):

q.qsize() 返回队列的大小
q.empty() 如果队列为空,返回True,反之False
q.full() 如果队列满了,返回True,反之False
q.full 与 maxsize 大小对应
q.get([block[, timeout]]) 获取队列,timeout等待时间
q.get_nowait() 相当q.get(False)非阻塞
q.put(item) 写入队列,timeout等待时间
q.put_nowait(item) 相当q.put(item, False)
q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
q.join() 实际上意味着等到队列为空,再执行别的操作 '''

join与task_done方法

'''
join() 阻塞进程,直到所有任务完成,需要配合另一个方法task_done。 def join(self):
with self.all_tasks_done:
while self.unfinished_tasks:
self.all_tasks_done.wait() task_done() 表示某个任务完成。每一条get语句后需要一条task_done。 import queue
q = queue.Queue()
q.put()
q.put()
print(q.get())
q.task_done()
print(q.get())
q.task_done() q.join() print("ending!")
'''

queue的三种模式:

1、queue.Queue()  先进先出模式

2、queue.LifoQueue()    先进后出,类似栈

3、queue.PriorityQueue()   优先级模式,优先级越高越先出,数字月底代表优先级越高

import queue

#######################先进后出
q=queue.LifoQueue() q.put(34)
q.put(56)
q.put(12) #####################优先级
q=queue.PriorityQueue()
q.put([5,100])
q.put([7,200])
q.put([3,"hello"])
q.put([4,{"name":"alex"}]) while 1:
data=q.get()
print(data)

队列的应用:生产者消费者模型

  在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

  生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

  这就像,在餐厅,厨师做好菜,不需要直接和客户交流,而是交给前台,而客户去饭菜也不需要不找厨师,直接去前台领取即可,这也是一个结耦的过程。

import queue,threading,time
import random q = queue.Queue(50) def Producer():
while True:
if q.qsize() < 20:
n = random.randint(1, 100)
q.put(n)
print(" has made baozi %s" % n)
time.sleep(1) def Consumer(id):
while True:
s = q.get()
print("Consumer"+id+"has eat %s" % s)
time.sleep(2) for i in range(5):
t1=threading.Thread(target=Producer,args=())
t1.start() for i in range(2):
t=threading.Thread(target=Consumer,args=(str(i),))
t.start()

  

  

Python进阶(3)_进程与线程中的lock(线程中互斥锁、递归锁、信号量、Event对象、队列queue)的更多相关文章

  1. Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量

    Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量 一丶线程的理论知识 什么是线程:    1.线程是一堆指令,是操作系统调度 ...

  2. setdeamon 设置 线程为守护线程, (lock线程锁, BoundedSemaphore,rlock递归锁 ) 三种锁

    1.setdeamon 当主程序执行完时,子程序自动被销毁 ,内存自动被收回 例一: import threading, time def run(n): print('run %s'%n) time ...

  3. python进阶十_正則表達式(一)

    近期状态一直不太好,至于原因,怎么说呢,不好说,总之就是纠结中覆盖着纠结,心思全然不在点上,希望能够借助Python的学习以及博客的撰写来调整回来,有的时候回头想一想,假设真的是我自己的问题呢,曾经我 ...

  4. python进阶八_警告和异常

    心情有点纠结,怎么说呢,倒不是由于其它学习上的事情,反而是由于生活上狗血的剧情逼着人偏离,渐行渐远,人跟人之间有误会也是正常的,可能是由于交流不够,彼此不够了解吧,希望能尽快度过这一段纠结的日子,简单 ...

  5. Python学习笔记_从CSV读取数据写入Excel文件中

    本示例特点: 1.读取CSV,写入Excel 2.读取CSV里具体行.具体列,具体行列的值 一.系统环境 1. OS:Win10 64位英文版 2. Python 3.7 3. 使用第三方库:csv. ...

  6. python进阶九_网络编程

    Python网络编程一 一.一些基本概念 在Python网络编程这一节中会涉及到非常多网络相关的术语.对于一些最主要的概念,如TCP/IP,Socket等等不再赘述,不明确的能够自己去查一查,对于一些 ...

  7. Python进阶学习_连接操作Redis数据库

    安装导入第三方模块Redis pip3 install redis import redis 操作String类型 """ redis 基本命令 String set(n ...

  8. python threading编程中的LOCK和RLOCK(可重入锁)

    找到一本PYTHON并发编辑的书, 弄弄.. #!/usr/bin/env python # -*- coding: utf-8 -*- import threading import time sh ...

  9. Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就绪,挂起,运行) ,***协程概念,yield模拟并发(有缺陷),Greenlet模块(手动切换),Gevent(协程并发)

    Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就 ...

  10. python进阶-------进程线程(二)

    Python中的进程线程(二) 一.python中的"锁" 1.GIL锁(全局解释锁) 含义: Python中的线程是操作系统的原生线程,Python虚拟机使用一个全局解释器锁(G ...

随机推荐

  1. awk 截取字符串

    1.把字符串的变量存入到其他变量中 1.1.编辑 shell 文件 [root@m910-120 test]# vi awkTest.sh ips=10.0.204.217:10.0.204.218 ...

  2. Java Resource路径小结

    首先一点很重要,Java中不存在标准的相对路径,各种相对路径取资源的方式都是基于某种规则转化为绝对路劲 然后一点也很重要,绝对不要直接使用绝对路径,否则死得很难看 基于以上两点,总结Resource路 ...

  3. Masonry介绍与使用实践:快速上手Autolayout【转载】

    MagicNumber -> autoresizingMask -> autolayout 以上是纯手写代码所经历的关于页面布局的三个时期 在iphone1-iphone3gs时代 win ...

  4. 创建动作-Action:

    在Struts2的行动,唯一的要求是,必须有一个无参数的方法,该方法返回一个字符串或结果的对象,必须是一个POJO.如果不带参数的方法不指定,则默认行为是使用execute()方法. 您也可以选择扩展 ...

  5. JSP接口浅析

    一.tree型关系 JSP页面继承了org.apache.jasper.runtime.HttpJspBase抽象类并实现了org.apache.jasper.runtime.JspSourceDep ...

  6. spring入门之JdbcTemplate 操作crud

    Spring 通过调用 JdbcTemplate来实现对数据库的增删改查,主要用到JdbcTemplate类的4个方法,首先,配置数据库信息,创建对象,代码通用: //设置数据库信息 DriverMa ...

  7. solr-in-action-ch4-Configuring Solr

    Solr基本的三个XML配置文件: solr.xml: solr 日志.shard.solrcould等配置 solrconfig.xml: 某个solr core的配置 schema.xml:某个s ...

  8. redis配置详解(中英文)

    V2.8.21: (中英字幕同步) # Redis configuration file example#* Redis 配置文件例子 # Note on units: when memory siz ...

  9. 加号选择器(ul>li + li)

    <head> <meta charset="UTF-8"> <title>+ selector</title> <style& ...

  10. EditTextView

    package com.egojit.android.sops.views.EditText; import android.content.Context; import android.graph ...