多进程进阶

一 multiprocessing模块介绍

  python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。Python提供了multiprocessing。
    multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。

  multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

    需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。

 

二 Process类

构造方法:

Process([group [, target [, name [, args [, kwargs]]]]])

  group: 线程组,目前还没有实现,库引用中提示必须是None; 
  target: 要执行的方法; 
  name: 进程名; 
  args/kwargs: 要传入方法的参数。

实例方法:

  is_alive():返回进程是否在运行。

  join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。

  start():进程准备就绪,等待CPU调度

  run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。

  terminate():不管任务是否完成,立即停止工作进程

属性:

  daemon:和线程的setDeamon功能一样

  name:进程名字。

  pid:进程号。

 

三 Process类的使用

创建并开启子进程的两种方式

import time
import random
from multiprocessing import Processdef foo(name):print('%s start' %name)time.sleep(random.randrange(1,6))print('%s end' %name)p1 = Process(target=foo,args=('Tom',)) #必须加 逗号
p2 = Process(target=foo,args=('Jerry',))
p3 = Process(target=foo,args=('Guido',))if __name__ == '__main__':p1.start()p2.start()p3.start()print('主线程')
方法一
import time
import random
from multiprocessing import Processclass Foo(Process):def __init__(self,name):super().__init__()self.name = namedef run(self):print('%s start' %self.name)time.sleep(random.randrange(1,6))print('%s ---> end' %self.name)p1 = Foo('Tom')
p2 = Foo('Jerry')
p3 = Foo('Guido')if __name__ == '__main__':p1.start() #start会自动调用run
    p2.start()p3.start()print('主线程')
方式二

 

进程之间的内存空间是隔离的

from multiprocessing import Processn = 100 #在windows系统中应该把全局变量定义在if __name__ == '__main__'之上就可以了def work():global nn = 0print('子进程:',n)if __name__ == '__main__':p = Process(target=work)p.start()print('主进程:',n)#结果:
主进程: 100
子进程: 0
View Code

 

Process对象的join方法

import time
import random
from multiprocessing import Processclass Foo(Process):def __init__(self,name):super().__init__()self.name = namedef run(self):print('%s start' %self.name)time.sleep(random.randrange(1,6))print('%s ---> end' %self.name)p = Foo('Tom')if __name__ == '__main__':p.start()p.join(0.0001) #等待p停止,等0.0001秒就不再等了print('开始')
join:主进程等,等待子进程结束
import time
import random
from multiprocessing import Processdef foo(name):print('%s start' %name)time.sleep(random.randrange(1,6))print('%s ---> end' %name)p1 = Process(target=foo,args=('Tom',)) #必须加 逗号
p2 = Process(target=foo,args=('Jerry',))
p3 = Process(target=foo,args=('Guido',))if __name__ == '__main__':p1.start()p2.start()p3.start()# 疑问:既然join是等待进程结束,那么我像下面这样写,进程不就又变成串行的了吗?# 当然不是了,必须明确:p.join()是让谁等?# 很明显p.join()是让主线程等待p的结束,卡住的是主线程而绝非进程p,# 详细解析如下:# 进程只要start就会在开始运行了,所以p1-p3.start()时,系统中已经有四个并发的进程了# 而我们p1.join()是在等p1结束,没错p1只要不结束主线程就会一直卡在原地,这也是问题的关键# join是让主线程等,而p1-p3仍然是并发执行的,p1.join的时候,其余p2,p3仍然在运行,等#p1.join结束,可能p2,p3,p4早已经结束了,这样p2.join,p3.join直接通过检测,无需等待# 所以3个join花费的总时间仍然是耗费时间最长的那个进程运行的时间
p1.join()p2.join()p3.join()print('主线程')#上述启动进程与join进程可以简写为
p_l = [p1,p2,p3]for p in p_l:p.start()for p in p_l:p.join()
有了join,程序不就是串行了吗?

 

注意:在windows中Process()必须放到# if __name__ == '__main__':下

Since Windows has no fork, the multiprocessing module starts a new Python process and imports the calling module. 
If Process() gets called upon import, then this sets off an infinite succession of new processes (or until your machine runs out of resources). 
This is the reason for hiding calls to Process() insideif __name__ == "__main__"
since statements inside this if-statement will not get called upon import.
由于Windows没有fork,多处理模块启动一个新的Python进程并导入调用模块。 
如果在导入时调用Process(),那么这将启动无限继承的新进程(或直到机器耗尽资源)。 
这是隐藏对Process()内部调用的原,使用if __name__ == “__main __”,这个if语句中的语句将不会在导入时被调用。
解释

 

四 守护进程

主进程创建守护进程 

其一:守护进程会在主进程代码执行结束后就终止
其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止

 

import time
import random
from multiprocessing import Processclass Foo(Process):def __init__(self,name):super().__init__()self.name = namedef run(self):print('%s start' %self.name)time.sleep(random.randrange(1,6))print('%s ---> end' %self.name)p = Foo('Tom')if __name__ == '__main__':p.daemon = True #一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程代码执行结束,p即终止运行
    p.start()print('主进程')
View Code
#主进程代码运行完毕,守护进程就会结束
from multiprocessing import Process
import time
def foo():print(123)time.sleep(1)print("end123")def bar():print(456)time.sleep(3)print("end456")p1=Process(target=foo)
p2=Process(target=bar)p1.daemon=True
p1.start()
p2.start()
print("main-------") #打印该行则主进程代码结束,则守护进程p1应该被终止,可能会有p1任务执行的打印信息123,因为主进程打印main----时,p1也执行了,但是随即被终止
迷惑人的例子

 

五 进程同步(锁)

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

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

 

part1:多个进程共享同一打印终端

#并发运行,效率高,但竞争同一打印终端,带来了打印错乱
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()#结果:
16924 is running
14620 is running
8640 is running
8640 is done
14620 is done
16924 is done
并发运行:效率高,但竞争统一打印终端,带来了打印错乱
#由并发变成了串行,牺牲了运行效率,但避免了竞争
from multiprocessing import Process,Lock
import os,time
def work(lock):lock.acquire()print('%s is running' %os.getpid())time.sleep(2)print('%s is done' %os.getpid())lock.release()
if __name__ == '__main__':lock=Lock()for i in range(3):p=Process(target=work,args=(lock,))p.start()#结果
11496 is running
11496 is done
13344 is running
13344 is done
11792 is running
11792 is done
加锁:由并发变成了串行,牺牲了运行效率,但避免了竞争

part2:多个进程共享同一文件

文件当数据库,模拟抢票

#文件db的内容为:{"count":1}
#注意一定要用双引号,不然json无法识别
from multiprocessing import Process,Lock
import time,json,random
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']-=1time.sleep(0.2) #模拟写数据的网络延迟json.dump(dic,open('db.txt','w'))print('\033[43m购票成功\033[0m')def task(lock):search()get()
if __name__ == '__main__':lock=Lock()for i in range(100): #模拟并发100个客户端抢票p=Process(target=task,args=(lock,))p.start()
并发运行,效率高,但竞争同一文件,数据写入错乱
#文件db的内容为:{"count":1}
#注意一定要用双引号,不然json无法识别
from multiprocessing import Process,Lock
import time,json,random
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']-=1time.sleep(0.2) #模拟写数据的网络延迟json.dump(dic,open('db.txt','w'))print('\033[43m购票成功\033[0m')def task(lock):search()lock.acquire()get()lock.release()
if __name__ == '__main__':lock=Lock()for i in range(100): #模拟并发100个客户端抢票p=Process(target=task,args=(lock,))p.start()
加锁:购票行为由并发变成了串行,牺牲了运行效率,但保证了数据安全

 

总结

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

 

六 队列(推荐使用)

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

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

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

    参数介绍:

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

  方法介绍:

主要方法:
1 q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为
正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue
已满,会立即抛出Queue.Full异常。
2 q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且
timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一
个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
3 q.get_nowait():同q.get(False) 4 q.put_nowait():同q.put(False)5 q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。6 q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。7 q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样

其他方法(了解):

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

 

应用:
from multiprocessing import Queue
q = Queue(3)q.put(1)
q.put(2)
q.put(3)
print(q.full())  #满了print(q.get())
print(q.get())
print(q.get())
print(q.empty())  #空了
View Code

 

生产者消费者模型

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

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

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

什么是生产者消费者模式

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

 

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

import os
import time
import random
from multiprocessing import Process,Queuedef 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' %iq.put(res)print('\033[42m%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('主线程')
View Code

 

#生产者消费者模型总结#程序中有两类角色
        一类负责生产数据(生产者)一类负责处理数据(消费者)#引入生产者消费者模型为了解决的问题是:
        平衡生产者与消费者之间的速度差#如何实现:生产者 -》队列 ——》消费者#生产者消费者模型实现类程序的解耦和

 

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

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

import os
import time
import random
from multiprocessing import Process,Queuedef 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' %iq.put(res)print('\033[42m%s 生产了 %s\033[0m' % (os.getpid(), res))q.put(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 os
import time
import random
from multiprocessing import Process,Queuedef 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' %iq.put(res)print('\033[42m%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()p1.join()q.put(None) #发送结束信号print('主线程')
主进程在生产者生产完毕后发送结束信号None

但上述解决方式,在有多个生产者和多个消费者时,我们则需要用一个很low的方式去解决

from multiprocessing import Process,Queue
import time,random,os
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(name,q):for i in range(2):time.sleep(random.randint(1,3))res='%s%s' %(name,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))p2=Process(target=producer,args=('骨头',q))p3=Process(target=producer,args=('泔水',q))#消费者们:即吃货们c1=Process(target=consumer,args=(q,))c2=Process(target=consumer,args=(q,))#开始
    p1.start()p2.start()p3.start()c1.start()p1.join() #必须保证生产者全部生产完毕,才应该发送结束信号
    p2.join()p3.join()q.put(None) #有几个消费者就应该发送几次结束信号Noneq.put(None) #发送结束信号print('')
有几个消费者就需要发送几次结束信号:相当low

 

其实我们的思路无非是发送结束信号而已,有另外一种队列提供了这种机制

#JoinableQueue([maxsize]):这就像是一个Queue对象,但队列允许项目的使用者通知生成者项目已经被成功处理。通知进程是使用共享的
信号和条件变量来实现的。
#参数介绍: maxsize是队列中允许最大项数,省略则无大小限制。 #方法介绍: JoinableQueue的实例p除了与Queue对象相同的方法之外还具有:q.task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数
量,将引发ValueError异常q.join():生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。阻塞将持续到队列中的每个项目均调用q.task_done()方法
为止
import os
import random
import time
from multiprocessing import Process,JoinableQueuedef 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))q.task_done() #向q.join()发送一次信号,证明一个数据已经取走了def producer(name,q):for i in range(3):time.sleep(random.randint(1,3))res='%s%s' %(name,i)q.put(res)print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))q.join()if __name__ == '__main__':q=JoinableQueue()#生产者们:即厨师们p1=Process(target=producer,args=('包子',q))p2=Process(target=producer,args=('骨头',q))p3=Process(target=producer,args=('泔水',q))#消费者们:即吃货们c1=Process(target=consumer,args=(q,))c2=Process(target=consumer,args=(q,))c1.daemon = Truec2.daemon = True#开始p_l = [p1,p2,p3,c1,c2]for p in p_l:p.start()p1.join() #必须保证生产者全部生产完毕,才应该发送结束信号
    p2.join()p3.join()print('')# 主进程等--->p1,p2 p3等---->c1,c2# p1,p2,p3结束了,证明c1,c2肯定全都收完了p1,p2,p3发到队列的数据# 因而c1,c2也没有存在的价值了,应该随着主进程的结束而结束,所以设置成守护进程
View Code

 

七 数据共享

展望未来,基于消息传递的并发编程是大势所趋

即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合

通过消息队列交换数据。这样极大地减少了对使用锁定和其他同步手段的需求,还可以扩展到分布式系统中

进程间通信应该尽量避免使用本节所讲的共享数据的方式

 
进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的虽然进程间数据独立,但可以通过Manager实现数据共享,事实上Manager的功能远不止于此A manager object returned by Manager() controls a server process which holds Python objects and allows other
processes to manipulate them using proxies.A manager returned by Manager() will support types list, dict, Namespace, Lock, RLock, Semaphore,
BoundedSemaphore, Condition, Event, Barrier, Queue, Value
and Array. For example,
from multiprocessing import Manager,Process,Lockdef work(d,lock):with lock: #不加锁而操作共享的数据,肯定会出现数据错乱d['count']-=1if __name__ == '__main__':lock=Lock()with Manager() as m:dic=m.dict({'count':100})p_l=[]for i in range(100):p=Process(target=work,args=(dic,lock))p_l.append(p)p.start()for p in p_l:p.join()print(dic)
from multiprocessing import Manager,Process,Lockdef work(d,lock):with lock: #不加锁而操作共享的数据,肯定会出现数据错乱d['count']-=1if __name__ == '__main__':lock=Lock()with Manager() as m:dic=m.dict({'count':100})p_l=[]for i in range(100):p=Process(target=work,args=(dic,lock))p_l.append(p)p.start()for p in p_l:p.join()print(dic)
进程之间操作共享数据

 

 

转载于:https://www.cnblogs.com/zhaochangbo/p/7834343.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/415326.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

typescript 安装

记录基本的typescript 安装过程。 1、安装 查看最新的版本号 npm show typescript去npm服务器读取包最新的信息: npm install -g typescript使用tsc -v查看版本号 使用npm bin -g查看安装目录 2、使用tsc index.ts就可以将ts文件编译成js文件。 官方在线实练…

linux中修改字符编码

一. ubuntu修改字符编码 1. 添加字符编码,例如zh_CN.UTF-8,有两种方式 方法1:locale-gen zh_CN.UTF-8 #locale-gen命令只在ubuntu中才有 方法2:在/var/lib/locales/supported.d/local中添加字符集zh_CN.UTF-8,保存后…

iview代码片段 去除水平menu底部横线

此处记录日常写的小代码片段 直接使用iview的Menu在底部会出现一条border线,而我的使用场景,不需要border线。所以我就覆盖一下它原来的样式,就可以了。 代码: .ivu-menu-light {&:after {height: 0px !important;}}效果&a…

工作161:eachart渲染步骤

第一步 页面 第二步 第三步 方法调用

vue js xlsx 读取 本地 excel

最近在写一个人力资源管理系统,主要目的是将本地的excel文件读取解析,将有效数据传到后台。 下文介绍流程,文末附有参考文章。 1、安装npm包xlsx yarn add xlsx2、结合上传组件和自己写的读取方法readXLSX,可以进行本地excel读…

工作163:eachrt修改

<template><div><el-card><el-card><time-selects GetTime"GetTime"></time-selects><!--第一步 页面--><div id"main" :style"{ width: 1000px, height: 300px }"></div><div id&quo…

Android 使用ViewPager实现view轮播效果,单个item分页样式,多个item分页样式,横向listview

效果 单个item样式 多个item样式 横向item 自定义viewpager——可开关切换动画 public class NoAnimationViewPager extends ViewPager {public NoAnimationViewPager(Context context) {super(context);}public NoAnimationViewPager(Context context, AttributeSet attrs) …

工作164:对父和混入的理解

1点击按钮看编辑事件 2点击编辑之后 对当前页面的方法进行调用 3找到子组件下面的混入方法 create下面引入混入 4找到子组件混入下面的混入方法 5找到下面的getlist方法

工作165:混入调用的时候

直接created里面定义 不需要在其他位置进行过多操作

工作166:错误的处理方式

1这种处理没有数据 2 这样处理勉强调用了接口 但是获取数据还是有问题

07异常处理

1、 【请尝试解释以下奇怪的现象&#xff01;】 int i1, j0, k; ki/j; 代码在运行时会引发异常 上边代码在运行时不会引发异常 浮点数除以0&#xff1a;Infinity 可以使用javap去反汇编两个示例程序的.class文件&#xff08;一个是AboutException.class&#xff0c;另一个是Thr…

工作166:正确eachrt渲染方式

1第一步 2第二步 3第三步

Effective C++: 06继承与面向对象设计

32&#xff1a;确定你的public继承塑模出is-a关系 以C进行面向对象编程&#xff0c;最重要的一个规则是&#xff1a;public继承表示的是"is-a"&#xff08;是一种&#xff09;的关系。 如果令class D以public形式继承class B&#xff0c;你便是告诉编译器说&#xff…

工作168:代码中的删除逻辑处理

1首先找到对应方法里面的删除方法 2点击之后进行删除操作 在当前方法里面直接进行混入删除 删除结束

Android侧滑删除-RecyclerView轻松实现高效的侧滑菜单

1 删除整个RecyclerView hisList.clear(); hisAdapter.notifyDataSetChanged(); mScanListAdapter.setNewData(null); 刚开始只是使用了list.clear() 方法就是没有效果&#xff0c;最后想起来了忘记了notifyDataSetChanged 刷新了 2 RecyclerView 删除Item hisList.remov…

前端学习(2645):懂代码之header表头页之未读消息

<!-- 消息中心 --><div class"btn-bell"><el-tooltipeffect"dark":content"message?有${message}条未读消息:消息中心"placement"bottom"><router-link to"/tabs"><i class"el-icon-bell&…

工作173:一级控制二级菜单的变化

1第一步 组件封装 利用$emit触发 触发结束 页面进行渲染 <!--封装部门选择的插件 需要的组件 子组件--> <template><el-select :value"value" placeholder"请选择所属部门" change"handleChange"><el-optionv-for&quo…

工作169:vue项目报错[Vue warn]: Property “visible“ must be accessed with “$data.visible“ because properties

vue项目中报错&#xff1a; [Vue warn]: Property "visible" must be accessed with "$data.visible" because properties starting with "$" or "_" are not proxied in the Vue instance to prevent conflicts with Vue internalsS…