Python之路(第三十八篇) 并发编程:进程同步锁/互斥锁、信号量、事件、队列、生产者消费者模型

一、进程锁(同步锁/互斥锁)

进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,

而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理。

例子

  #并发运行,效率高,但竞争同一打印终端,带来了打印错乱
  from multiprocessing import Process
  import os,time
  def work():
      print(‘%s is running‘ %os.getpid())
      time.sleep(2)
      print(‘%s is done‘ %os.getpid())
  ?
  if __name__ == ‘__main__‘:
      for i in range(3):
          p=Process(target=work)
          p.start()

  

加锁后

  #加锁后由并发变成了串行,牺牲了运行效率,但避免了竞争
  ?
  from multiprocessing import Process,Lock
  import os,time
  def work(mutex):
      mutex.acquire() #开始加锁
      print(‘%s is running‘ %os.getpid())
      time.sleep(2)
      print(‘%s is done‘ %os.getpid())
      mutex.release() #释放锁,在加锁期间别的进程都要等
  ?
  if __name__ == ‘__main__‘:
      mutex = Lock()
      for i in range(3):
          p=Process(target=work,args=(mutex,))
          p.start()

  

例子2

多个进程共享同一文件

文件当数据库,模拟抢票

未加锁版

  
  #文件db.txt的内容为:{"count":1}
  #注意一定要用双引号,不然json无法识别
  ?
  # 并发运行,效率高,但竞争写同一文件,数据写入错乱
  from multiprocessing import Process,Lock
  import time,json,random,os
  def search():
      dic=json.load(open(‘db.txt‘))
      print(‘\033[43m剩余票数%s\033[0m‘ %dic[‘count‘])
  ?
  def get():
      dic=json.load(open(‘db.txt‘))
      time.sleep(0.1) #模拟读数据的网络延迟
      if dic[‘count‘] >0:
          dic[‘count‘]-=1
          time.sleep(0.2) #模拟写数据的网络延迟
          json.dump(dic,open(‘db.txt‘,‘w‘))
          print(‘%s\033[43m购票成功\033[0m‘%(os.getpid()))
  ?
  def task(lock):
      search()
      get()
  if __name__ == ‘__main__‘:
      lock=Lock()
      for i in range(10): #模拟并发10个客户端抢票
          p=Process(target=task,args=(lock,))
          p.start()

  

输出结果

  
  剩余票数1
  剩余票数1
  剩余票数1
  剩余票数1
  剩余票数1
  剩余票数1
  剩余票数1
  剩余票数1
  剩余票数1
  剩余票数1
  4120购票成功
  2692购票成功
  7328购票成功
  13444购票成功
  13632购票成功
  13560购票成功
  13752购票成功
  12564购票成功
  13720购票成功
  13488购票成功

  

加锁版

  import multiprocessing,time,json,random
  ?
  def search(name):
      with open("db.txt","r",encoding="utf-8") as f:
          data_dic = json.load(f)
          time.sleep(random.uniform(0,2))
          if data_dic["count"] >= 1 :
              print("已查询到票还有%s张,当前系统时间 %s"%(data_dic["count"],time.asctime()))
          else:
              print("系统票源不足!当前系统时间 %s"%time.asctime())
  ?
  def buy(name):
      with open("db.txt","r+",encoding="utf-8") as f:
          data_dic = json.load(f)
      if data_dic["count"] > 0 :
          with open("db.txt", "w", encoding="utf-8") as g:
              new_ticket_count = data_dic["count"] - 1
              data_dic.update({"count":new_ticket_count})
              json.dump(data_dic,g)
          print("%s购票成功!"%name)
      else:
          print("%s购票失败!"%name)
  ?
  def task(name,mutex):
      search(name)  # 查询无需加锁
      mutex.acquire()
      buy(name)  #针对修改文件的关键操作加锁
      mutex.release()
  ?
  ?
  if __name__ == "__main__":
      mutex = multiprocessing.Lock()
      for i in range(10):
          p = multiprocessing.Process(target=task,args=("乘客%s"%i,mutex))
          p.start()

  

分析

  
  #加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。
  虽然可以用文件共享数据实现进程间通信,但问题是:
  1.效率低(共享数据基于文件,而文件是硬盘上的数据)
  2.需要自己加锁处理
  ?
  ?
  ?
  #因此我们最好找寻一种解决方案能够兼顾:1、效率高(多个进程共享一块内存的数据)2、帮我们处理好锁问题。这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。
  1 队列和管道都是将数据存放于内存中
  2 队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,
  我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。

  

二、信号量(multiprocess.Semaphore)

互斥锁同时只允许一个线程更改数据,而信号量Semaphore是同时允许一定数量的线程更改数据 。实现:信号量同步基于内部计数器,每调用一次acquire(),计数器减1;每调用一次release(),计数器加1.当计数器为0时,acquire()调用被阻塞。这是迪科斯彻(Dijkstra)信号量概念P()和V()的Python实现。信号量同步机制适用于访问像服务器这样的有限资源。信号量与进程池的概念很像,但是要区分开,信号量涉及到加锁的概念。

例子

  
  # 多进程中的组件
  # ktv
  # 4个
  # 一套资源  同一时间 只能被n个人访问
  # 某一段代码 同一时间 只能被n个进程执行
  import time
  import random
  from multiprocessing import Process
  from multiprocessing import Semaphore
  ?
  # sem = Semaphore(4)
  # sem.acquire()
  # print(‘拿到第一把钥匙‘)
  # sem.acquire()
  # print(‘拿到第二把钥匙‘)
  # sem.acquire()
  # print(‘拿到第三把钥匙‘)
  # sem.acquire()
  # print(‘拿到第四把钥匙‘)
  # sem.acquire()
  # print(‘拿到第五把钥匙‘)
  def ktv(i,sem):
      sem.acquire()    #获取钥匙
      print(‘%s走进ktv‘%i)
      time.sleep(random.randint(1,5))
      print(‘%s走出ktv‘%i)
      sem.release()
  ?
  ?
  if __name__ == ‘__main__‘ :
      sem = Semaphore(4)
      for i in range(20):
          p = Process(target=ktv,args=(i,sem))
          p.start()

  

三、事件(multiprocess.Event)

python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。

clear:将“Flag”设置为False,set:将“Flag”设置为True.

例子

  ?
  from multiprocessing import Event
  ?
  e = Event()
  print(e.is_set()) #初始设置为False
  print("数据111")
  e.set()  #设置之后为True
  print("数据222")
  print(e.is_set()) #打印设置之后的状态
  e.wait()  #当值为False会阻塞,当值为Ture是,不会阻塞
  print("数据333")
  e.clear() #清除事件状态,设置为False
  print(e.is_set())  #打印清除之后的状态
  print("数据444")
  e.wait()  #此时值为False,程序会一直阻塞
  print("数据555")

  

 

输出结果

  
  False
  数据111
  数据222
  True
  数据333
  False
  数据444

  

例子

简单的红绿灯事件

  
  from multiprocessing import Event,Process
  import time
  import random
  ?
  ?
  def cars(e,num):
      if not e.is_set(): # 进程刚开启,is_set()的值是False,模拟信号灯为红色
          print("%s车正在等待通行"%num)
          e.wait() # 阻塞,等待信号灯切换
      print("%s车已经通过" % num) #打印已经通过的进程
  ?
  ?
  def light(e):
  ?
      #模拟定时切换红绿灯
      while True:
          if e.is_set():
              e.clear() #>将is_set()的值设置为False
              print("\033[31m红灯亮了\033[0m")
          else:
              e.set() #>将is_set()的值设置为True
              print("\033[32m绿灯亮了\033[0m")
          time.sleep(2)
  ?
  if __name__ == "__main__":
      e = Event()
      traffic = Process(target=light,args=(e,))
      traffic.start() #启动红绿灯进程
      for i in range(20):
          car = Process(target=cars,args=(e,"布加迪%s"%i))
          car.start()
          time.sleep(random.random())

  

四、进程间通信——队列和管道

进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的

队列

 队列就相当于一个容器,里面可以放数据,特点是先放进去先拿出来,即先进先出。

创建队列的类(底层就是以管道和锁定的方式实现)

  
  Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。

  

参数

  
  maxsize是队列中允许最大项数,省略则无大小限制。

  

  方法介绍:

  q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
  q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.

  q.get_nowait():同q.get(False)
  q.put_nowait():同q.put(False)
  ?
  q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
  q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
  q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样

  

其他方法(了解):

  q.close()
  关闭队列,防止队列中加入更多数据。调用此方法时,后台线程将继续写入那些已入队列但尚未写入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将自动调用此方法。关闭队列不会在队列使用者中生成任何类型的数据结束信号或异常。例如,如果某个使用者正被阻塞在get()操作上,关闭生产者中的队列不会导致get()方法返回错误。
  ?
  q.cancel_join_thread()
  不会再进程退出时自动连接后台线程。这可以防止join_thread()方法阻塞。
  ?
  q.join_thread()
  连接队列的后台线程。此方法用于在调用q.close()方法后,等待所有队列项被消耗。默认情况下,此方法由不是q的原始创建者的所有进程调用。调用q.cancel_join_thread()方法可以禁止这种行为。

  

例子

  from multiprocessing import Queue
  ?
  q = Queue(3)  # 创建一个队列对象,并给他设置容器大小,即能放几个数据
  q.put(1)  # put()方法是往容器里放数据
  q.put([2,3])
  q.put({"k1":4})
  # q.put("mi") # 如果队列已经满了,程序就会停在这里,等待数据被别人取走,再将数据放入队列。
  try:
      q.put_nowait(3) # 可以使用put_nowait,如果队列满了不会阻塞,但是会因为队列满了而报错。
  except: # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去,但是会丢掉这个消息。
      print(‘队列已经满了‘)
  ?
  # 因此,我们再放入数据之前,可以先看一下队列的状态,如果已经满了,就不继续put了。
  print(q.full()) #返回True ,满了
  print(q.get())  #get()方法是从容器里拿数据
  print(q.get())
  print(q.get())
  # 同put方法一样,如果队列已经空了,那么继续取就会出现阻塞。
  try:
      q.get_nowait() # 可以使用get_nowait,如果队列满了不会阻塞,但是会因为没取到值而报错。
  except: # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去。
      print(‘队列已经空了‘)
  ?
  print(q.empty()) #空了

  

例子

  
  import time
  from multiprocessing import Queue, Process
  ?
  ?
  def task(q):
      q.put(" hello! 时间%s"%time.asctime())  # 调用主函数中p进程传递过来的进程参数 put函数为向队列中添加一条数据。
  ?
  ?
  if __name__ == ‘__main__‘:
      q = Queue(3)#创建一个Queue对象
      p = Process(target=task, args=(q,)) #创建一个子进程
      p.start()
      print(q.get()) #在主进程打印从子进程获取的数据

  

      

生产者消费者模型

生产者消费者模型

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。

为什么要使用生产者消费者模型

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

什么是生产者和消费者模式

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

基于队列实现生产者消费者模型

  
  from multiprocessing import Process, Queue
  import time, random, os
  ?
  ?
  def consumer(q):
      while True:
          res = q.get()
          time.sleep(random.randint(1, 3))
          print(‘\033[45m%s 吃 %s\033[0m‘ % (os.getpid(), res))
  ?
  ?
  def producer(q):
      for i in range(10):
          time.sleep(random.randint(1, 3))
          res = ‘包子%s‘ % i
          q.put(res)
          print(‘\033[44m%s 生产了 %s\033[0m‘ % (os.getpid(), res))
  ?
  ?
  if __name__ == ‘__main__‘:
      q = Queue()
      # 生产者们:即厨师们
      p1 = Process(target=producer, args=(q,))
  ?
      # 消费者们:即吃货们
      c1 = Process(target=consumer, args=(q,))
  ?
      # 开始
      p1.start()
      c1.start()
      print(‘主‘)

  

生产者消费者模型总结

  #程序中有两类角色
      一类负责生产数据(生产者)
      一类负责处理数据(消费者)

  #引入生产者消费者模型为了解决的问题是:
      平衡生产者与消费者之间的工作能力,从而提高程序整体处理数据的速度

  #如何实现:
      生产者<-->队列<——>消费者
  #生产者消费者模型实现类程序的解耦和

  

此时的问题是主进程永远不会结束,原因是:生产者p在生产完后就结束了,但是消费者c在取空了q之后,则一直处于死循环中且卡在q.get()这一步。

解决方式无非是让生产者在生产完毕后,往队列中再发一个结束信号,这样消费者在接收到结束信号后就可以break出死循环

  import time, random, os
  from multiprocessing import Process, Queue
  ?
  ?
  def consumer(q):
      while True:
          res = q.get()
          if res is None: break  # 收到结束信号则结束
          time.sleep(random.randint(1, 3))
          print(‘\033[45m%s 吃 %s\033[0m‘ % (os.getpid(), res))
  ?
  ?
  def producer(q):
      for i in range(10):
          time.sleep(random.randint(1, 3))
          res = ‘包子%s‘ % i
          q.put(res)
          print(‘\033[44m%s 生产了 %s\033[0m‘ % (os.getpid(), res))
      q.put(None)  # 发送结束信号,生产者在生产完毕后发送结束信号None
  ?
  ?
  if __name__ == ‘__main__‘:
      q = Queue()
      # 生产者们:即厨师们
      p1 = Process(target=producer, args=(q,))
  ?
      # 消费者们:即吃货们
      c1 = Process(target=consumer, args=(q,))
  ?
      # 开始
      p1.start()
      c1.start()
      print(‘主‘)

  

  

注意:结束信号None,不一定要由生产者发,主进程里同样可以发,但主进程需要等生产者结束后才应该发送该信号。但上述解决方式,在有多个生产者和多个消费者时,需要多次发送None信号。

  import multiprocessing
  import time
  import random
  ?
  ?
  def producer(name, q):
      for i in range(2):
          res = "包子%s" % i
          time.sleep(random.randint(0, 1))
          print("%s生产了%s" % (name, res))
          q.put(res)
  ?
  ?
  def consumer(name, q):
      while True:
          res = q.get()
          if q.get() is None:  # 收到结束信号则结束
              print("没包子吃了")
              break
          print("%s吃了%s" % (name, res))
  ?
  ?
  if __name__ == "__main__":
      q = multiprocessing.Queue()
      p1 = multiprocessing.Process(target=producer, args=("jack", q))
      p2 = multiprocessing.Process(target=producer, args=("charles", q))
      p3 = multiprocessing.Process(target=producer, args=("pony", q))
      c1 = multiprocessing.Process(target=consumer, args=("nick", q))
      c2 = multiprocessing.Process(target=consumer, args=("nicholas", q))
      p_list = []
      p_list.append(p1)
      p_list.append(p2)
      p_list.append(p3)
      for p in p_list:
          p.start()
      c1.start()
      c2.start()
      p1.join() #必须保证生产者全部生产完毕,才应该发送结束信号
      p2.join()
      p3.join()
      q.put(None)  # 发送结束信号,有几个消费者就应该发送几次结束信号None
      q.put(None)  # 发送结束信号
      print("end........")

  

  

这里有另外一种队列提供了这种机制,JoinableQueue。

JoinableQueue([maxsize])

其实就是一种队列,但又比队列要多两种方法,task_done()和join()方法,正是有这两种方法就可以解决上面的问题。

创建可连接的共享进程队列。这就像是一个Queue对象,但队列允许项目的使用者通知生产者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。

方法介绍

  
  JoinableQueue的实例p除了与Queue对象相同的方法之外,还具有以下方法:
  ?
  q.task_done()
  使用者使用此方法发出信号,表示q.get()返回的项目已经被处理。如果调用此方法的次数大于从队列中删除的项目数量,将引发ValueError异常。
  ?
  q.join()
  生产者将使用此方法进行阻塞,直到队列中所有项目均被处理。阻塞将持续到为队列中的每个项目均调用q.task_done()方法为止。
  下面的例子说明如何建立永远运行的进程,使用和处理队列上的项目。生产者将项目放入队列,并等待它们被处理。

  

例子

import multiprocessing
import time
import random

def producer(name, q):
    for i in range(2):
        res = "包子%s" % i
        time.sleep(random.randint(0, 1))
        print("%s生产了%s" % (name, res))
        q.put(res)
    q.join()  # 只有顾客把队列的包子全部拿走后,三个生产者进程才能全部结束

def consumer(name, q):
    while True:
        res = q.get()
        print("%s吃了%s" % (name, res))
        q.task_done()  # 发信号告诉队列,又吃完了一个,从队列中取走一个数据并处理完成

if __name__ == "__main__":
    # q = multiprocessing.Queue()
    q = multiprocessing.JoinableQueue()
    p1 = multiprocessing.Process(target=producer, args=("jack", q))
    p2 = multiprocessing.Process(target=producer, args=("charles", q))
    p3 = multiprocessing.Process(target=producer, args=("pony", q))
    c1 = multiprocessing.Process(target=consumer, args=("nick", q))
    c2 = multiprocessing.Process(target=consumer, args=("nicholas", q))
    p_list = []
    p_list.append(p1)
    p_list.append(p2)
    p_list.append(p3)
    for p in p_list:
        p.start()
    c1.daemon = True  # 将c1\c2设置成守护进程,只要主进程结束了,那么顾客就收到了所有的数据
    c2.daemon = True
    c1.start()
    c2.start()
    p1.join()
    p2.join()
    p3.join()

    print("end........")
# 主进程等--->p1,p2,p3等---->c1,c2
# p1,p2,p3结束了,证明c1,c2肯定全都收完了p1,p2,p3发到队列的数据
# 因而c1,c2也没有存在的价值了,不需要继续阻塞在进程中影响主进程了。
# 应该随着主进程的结束而结束,所以设置成守护进程就可以了。

  

原文地址:https://www.cnblogs.com/Nicholas0707/p/10203587.html

时间: 2024-11-10 02:10:54

Python之路(第三十八篇) 并发编程:进程同步锁/互斥锁、信号量、事件、队列、生产者消费者模型的相关文章

Python之路【第十八篇】:Web框架们

Python之路[第十八篇]:Web框架们 Python的WEB框架 Bottle Bottle是一个快速.简洁.轻量级的基于WSIG的微型Web框架,此框架只由一个 .py 文件,除了Python的标准库外,其不依赖任何其他模块. 1 2 3 4 pip install bottle easy_install bottle apt-get install python-bottle wget http://bottlepy.org/bottle.py Bottle框架大致可以分为以下部分: 路

【Python之路】第十八篇--MySQL(一)

[Python之路]第十八篇--MySQL(一) 一.概述 1.什么是数据库 ? 答:数据的仓库,如:在ATM的示例中我们创建了一个 db 目录,称其为数据库 2.什么是 MySQL.Oracle.SQLite.Access.MS SQL Server等 ? 答:他们均是一个软件,都有两个主要的功能: a. 将数据保存到文件或内存 b. 接收特定的命令,然后对文件进行相应的操作 PS:如果有了以上软件,无须自己再去创建文件和文件夹,而是直接传递 命令 给上述软件,让其来进行文件操作,他们统称为数

Python之路【第二十八篇】:django视图层、模块层

1.视图函数 文件在view_demo 一个视图函数简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应.响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以.无论视图本身包含什么逻辑,都要返回响应.代码写在哪里也无所谓,只要它在你的Python目录下面.除此之外没有更多的要求了——可以说“没有什么神奇的地方”.为了将代码放在某处,约定是将视图放置在项目或应用程序目录中的名为views.py的文件中. 视图

Python之路【第十八篇】Django小项目webQQ实现

WEBQQ的实现的几种方式 1.HTTP协议特点 首先这里要知道HTTP协议的特点:短链接.无状态! 在不考虑本地缓存的情况举例来说:咱们在连接博客园的时候,当tcp连接后,我会把我自己的http头发给博客园服务器,服务器端就会看到我请求的URL,server端就会根据URL分发到相应的视图处理(Django的views里).最后给我返回这个页面,当返回之后连接就断开了. 短连接: 服务器为什么要断开?很多情况下我还会打开页面,我请求一次连接断开了为什么会这样?为什么不建立长期的连接?这个是HT

Python之路【第十八篇】Django小项目简单BBS论坛部分内容知识点

开发一个简单的BBS论坛 项目需求: 1 整体参考“抽屉新热榜” + “虎嗅网” 2 实现不同论坛版块 3 帖子列表展示 4 帖子评论数.点赞数展示 5 在线用户展示 6 允许登录用户发贴.评论.点赞 7 允许上传文件 8 帖子可被置顶 9 可进行多级评论 知识必备:(注:没有必备下面知识的同学,请返回去看会之后再看下面的内容防止蒙了~~!) 1 Django 2 HTML\CSS\JS 3 BootStrap 4 Jquery 设计表结构 1.表结构重要性 在开发任何项目的时候,设计到数据库,

Python之路【第十八篇】:前端HTML

一.前端概述 import socket def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost',8089)) sock.listen(5) while True: connection, address = sock.accept() buf = connection.recv(1024) connection.sendall(bytes("HTTP/1.1 201

Python之路,第十八篇:Python入门与基础18

python3  面向对象编程2 类方法: @classmethod 作用:1,类方法是只能访问类变量的方法: 2,类方法需要使用@classmethod 装饰器定义: 3,类方法的第一个参数是类的实例, 约定写成cls 说明:1, 类实例和对象实例都可以调用类方法: 2, 类方法不能方法实例变量 类方法和实例方法对比: 1,类方法能够访问类变量,不能访问实例变量: 实例方法能够访问类变量,也能访问实例变量 2,类方法可以用实例来调用,也可以用类来调用: 实例方法在调用时必须传入实例: 1 cl

Python之路(第三十九篇)管道、进程间数据共享Manager

一.管道 概念 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信. 先画一幅图帮助大家理解下管道的基本原理 现有2个进程A和B,他们都在内存中开辟了空间,那么我们在内存中再开辟一个空间C,作用是连接这两个进程的.对于进程来说内存空间是可以共享的(任何一个进程都可以使用内存,内存当中的空间是用地址来标记的,我们通过查找某一个地址就能找到这个内存)A进程可以不断的向C空间输送东西,B进程可以不断的从C空间读取东西,这

Python之路【第十六篇】:Django【基础篇】

Python之路[第十六篇]:Django[基础篇] Python的WEB框架有Django.Tornado.Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM.模型绑定.模板引擎.缓存.Session等诸多功能. 基本配置 一.创建django程序 终端命令:django-admin startproject sitename IDE创建Django程序时,本质上都是自动执行上述命令 其他常用命令: python manage.py runserver