进程与线程

进程

进程锁

进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理

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()

加锁:由并发变成了串行,牺牲了运行效率,但避免了竞争

# 由并发变成了串行,牺牲了运行效率,但避免了竞争
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()

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.需要自己加锁处理

因此我们最好找寻一种解决方案能够兼顾这两点的机制:这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。

1 队列和管道都是将数据存放于内存中


2 队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来

我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。

查看主进程和子进程的进程号

import os
from multiprocessing import Processdef f(x):print('子进程id :',os.getpid(),'父进程id :',os.getppid())return x*xif __name__ == '__main__':print('主进程id :', os.getpid())p_lst = []for i in range(5):p = Process(target=f, args=(i,))p.start()

进阶,多个进程同时运行(注意,子进程的执行顺序不是根据启动顺序决定的

进程之间的数据隔离问题

from multiprocessing import Processdef work():global nn=0print('子进程内: ',n)if __name__ == '__main__':n = 100p=Process(target=work)p.start()print('主进程内: ',n)
"""这两个进程之间的数据没有通信""

问题:如何让进程与进程之间数据通信?

队列

进程彼此之间互相隔离,要实现进程间通信(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()一样


其他方法(了解):

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

应用:

'''
multiprocessing模块支持进程间通信的两种主要形式:管道和队列
都是基于消息传递实现的,但是队列接口
'''from multiprocessing import Process,Queue
import time
q=Queue(3)# put ,get ,put_nowait,get_nowait,full,empty
q.put(3)
q.put(3)
q.put(3)
print(q.full()) # 满了print(q.get())
print(q.get())
print(q.get())
print(q.empty()) # 空了

生产者消费者模型

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

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

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

什么是生产者消费者模式

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

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

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' %iq.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出死循环

生产者在生产完毕后发送结束信号None

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(q):for i in range(10):time.sleep(random.randint(1,3))res='包子%s' %iq.put(res)print('\033[44m%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
 

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(q):for i in range(2):time.sleep(random.randint(1,3))res='包子%s' %iq.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()p1.join()q.put(None) #发送结束信号print('主')

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

有几个消费者就需要发送几次结束信号:相当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('主')

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

# JoinableQueue([maxsize]):这就像是一个Queue对象,但队列允许项目的使用者通知生成者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。# 参数介绍:maxsize是队列中允许最大项数,省略则无大小限制。    # 方法介绍:JoinableQueue的实例p除了与Queue对象相同的方法之外还具有:q.task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常q.join():生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。阻塞将持续到队列中的每个项目均调用q.task_done()方法为止
from multiprocessing import Process,JoinableQueue
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))q.task_done() #向q.join()发送一次信号,证明一个数据已经被取走了def producer(name,q):for i in range(10):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也没有存在的价值了,应该随着主进程的结束而结束,所以设置成守护进程

线程

开启线程的两种方式

方式一

#方式一
from threading import Thread
import time
def sayhi(name):time.sleep(2)print('%s say hello' %name)if __name__ == '__main__':t=Thread(target=sayhi,args=('egon',))t.start()print('主线程')

方式二

# 方式二
from threading import Thread
import time
class Sayhi(Thread):def __init__(self,name):super().__init__()self.name=namedef run(self):time.sleep(2)print('%s say hello' % self.name)if __name__ == '__main__':t = Sayhi('ly')t.start()print('主线程')

在一个进程下开启多个线程与在一个进程下开启多个子进程的区别 

1、谁的开启速度快 

from threading import Thread
from multiprocessing import Process
import osdef work():print('hello')if __name__ == '__main__':# 在主进程下开启线程t=Thread(target=work)t.start()print('主线程/主进程')'''打印结果:hello主线程/主进程'''# 在主进程下开启子进程t=Process(target=work)t.start()print('主线程/主进程')'''打印结果:主线程/主进程hello'''

2、瞅一瞅pid

from threading import Thread
from multiprocessing import Process
import osdef work():print('hello',os.getpid())if __name__ == '__main__':# part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样t1=Thread(target=work)t2=Thread(target=work)t1.start()t2.start()print('主线程/主进程pid',os.getpid())# part2:开多个进程,每个进程都有不同的pidp1=Process(target=work)p2=Process(target=work)p1.start()p2.start()print('主线程/主进程pid',os.getpid())

3、同一进程内的线程共享该进程的数据?

from  threading import Thread
from multiprocessing import Process
import os
def work():global nn=0if __name__ == '__main__':# n=100# p=Process(target=work)# p.start()# p.join()# print('主',n) #毫无疑问子进程p已经将自己的全局的n改成了0,但改的仅仅是它自己的,查看父进程的n仍然为100n=1t=Thread(target=work)t.start()t.join()print('主',n) #查看结果为0,因为同一进程内的线程之间共享进程内的数据

线程相关的其他方法

Thread实例对象的方法# isAlive(): 返回线程是否活动的。# getName(): 返回线程名。# setName(): 设置线程名。threading模块提供的一些方法:# threading.currentThread(): 返回当前的线程变量。# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
from threading import Thread
import threading
from multiprocessing import Process
import osdef work():import timetime.sleep(3)print(threading.current_thread().getName())if __name__ == '__main__':#在主进程下开启线程t=Thread(target=work)t.start()print(threading.current_thread().getName())print(threading.current_thread()) #主线程print(threading.enumerate()) #连同主线程在内有两个运行的线程print(threading.active_count())print('主线程/主进程')'''打印结果:MainThread<_MainThread(MainThread, started 140735268892672)>[<_MainThread(MainThread, started 140735268892672)>, <Thread(Thread-1, started 123145307557888)>]主线程/主进程Thread-1'''

主线程等待子线程结束

from threading import Thread
import time
def sayhi(name):time.sleep(2)print('%s say hello' %name)if __name__ == '__main__':t=Thread(target=sayhi,args=('ly',))t.start()t.join()print('主线程')print(t.is_alive())'''ly say hello主线程False'''

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

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

相关文章

【框架源码篇 03】Spring源码手写篇-手写AOP

Spring源码手写篇-手写AOP 手写IoC和DI后已经实现的类图结构。 一、AOP分析 1.AOP是什么? AOP[Aspect Oriented Programming] 面向切面编程&#xff0c;在不改变类的代码的情况下&#xff0c;对类方法进行功能的增强。 2.我们要做什么&#xff1f; 我们需要在前面手写IoC&…

排序算法,冒泡排序算法及优化,选择排序SelectionSort,快速排序(递归-分区)

一、冒泡排序算法&#xff1a; 介绍&#xff1a; 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单直观的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需…

关于SparkRdd和SparkSql的几个指标统计,scala语言,打包上传到spark集群,yarn模式运行

需求&#xff1a; ❖ 要求:分别用SparkRDD, SparkSQL两种编程方式完成下列数据分析,结合webUI监控比较性能优劣并给出结果的合理化解释. 1、分别统计用户&#xff0c;性别&#xff0c;职业的个数&#xff1a; 2、查看统计年龄分布情况&#xff08;按照年龄分段为7段&#xff0…

初识树结构和二叉树

一&#xff0c;树概念及结构 1.1树结构的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 注意&a…

AI全栈大模型工程师(九)Function Calling 的机制

文章目录 Function Calling 的机制Function Calling 示例 1:加法计算器Function Calling 实例 2:四则混合运算计算器后记Function Calling 的机制 Function Calling 示例 1:加法计算器 需求:用户输入任意可以用加法解决的问题,都能得到计算结果。 # 加载环境变量import o…

人工智能发展与结构科学

人工智能&#xff08;AI&#xff09;在各种应用中的影响力不断增强&#xff0c;从简单的计算任务到复杂的决策支持。但在这背后&#xff0c;AI的发展其实是一个关于结构演变的故事。从最早的线性结构&#xff0c;到今天的复杂网络结构&#xff0c;结构的演变对AI的能力和效率产…

【linux】查看下载应用在服务器的日志

查看日志路径 一般在配置文件中logback.xml 账号密码xshell连接服务器&#xff0c;进入日志路径 根据搜索关键字查看xxx.log文件内容 cat xxx.log | grep 关键字 下载 xxx.log 到本地&#xff0c;一般可以下载当天的日志文件到本地查看比较方便 sz xxx.log 参考文章&#xff…

Adobe 推出 Photoshop Elements 2024 新版

&#x1f989; AI新闻 &#x1f680; Adobe 推出 Photoshop Elements 2024 新版 摘要:Adobe 最新发布 Photoshop Elements 2024 版本,新增引入 AI 功能,提供匹配颜色、创建照片卷、一键选择照片天空或背景等新功能,界面也进行了优化更新。本次发布重点加强了 AI 支持,简化复杂…

【软考-中级】系统集成项目管理工程师 【19 项目收尾管理】

持续更新。。。。。。。。。。。。。。。 【第十九章】收尾管理 &#xff08;选择题1分&#xff09; 19.1 项目验收19.2 项目总结19.3系统维护19.3.1软件项目的后续工作19.3.2系统集成项目的后续工作 19.4 项目后评价1. 信息系统目标评价2. 信息系统过程评价3. 信息系统效益评价…

嵌入式学习笔记(60)内存管理之堆

1.7.1.什么是堆&#xff08;heap&#xff09; 内存管理对OS来说是一件非常复杂的事&#xff0c;因为首先内存容量大&#xff0c;其次内存需求在时间和大小块上没有规律&#xff08;OS上运行着几十、几百、几千个进程随时都会申请或者释放内存&#xff0c;申请或者释放的内存块…

leetcode:2678. 老人的数目(python3解法)

难度&#xff1a;简单 给你一个下标从 0 开始的字符串 details 。details 中每个元素都是一位乘客的信息&#xff0c;信息用长度为 15 的字符串表示&#xff0c;表示方式如下&#xff1a; 前十个字符是乘客的手机号码。接下来的一个字符是乘客的性别。接下来两个字符是乘客的年…

100天掌握网络安全知识点!

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟…

Microsoft Edge中使用开源的ChatGPT

一、双击打开浏览器 找到&#xff1a;扩展&#xff0c;打开 二、打开Microsoft Edge加载项 三、Move tab新标签 获取免费ChatGPT 四、启用Move tab。启用ChatGPT。 扩展 管理扩展 启用 五、新建标签页&#xff0c;使用GPT 六、使用举例 提问 GPT回复

学习git博客

git新建分支并且提交代码过程 1. git pull <codeBaseAddress> [分支名(默认是master)] 2. cd <projectName> 3. git branch <newBranchName> // 创建分支 4. git checkout <newBranchName> // 切换到新分支 // 开始写你的新代码 5. git add . //…

idea2023配置maven

看过【黑马程序员Maven全套教程&#xff0c;maven项目管理从基础到高级&#xff0c;Java项目开发必会管理工具maven】https://www.bilibili.com/video/BV1Ah411S7ZE?p9&vd_sourceedf9d91e5a0a27db51e3d6d4b9400637 配置的&#xff0c;前提要素配置也在这个课程里有啦&…

linux 下的java gate服务断掉的原因及解决思路

一.查询断掉的原因 1.查看gate日志&#xff0c;发现没有报错信息&#xff0c;突然就断了 2.查看是不是OOM导致 dmesg | grep java 发现确实Out of Memory了 3.发生问题的原因&#xff1a; 默认情况下, Linux kernels(内核)允许进程申请的量超过系统可用内存. 这是因为,在大多数…

微机原理:汇编语言语句类型与格式

文章目录 壹、语句类型1、语句分类2、常用伪代码和运算符2.1数据定义伪指令2.1.1字节定义伪指令DB&#xff08;8位&#xff09;2.1.2字定义伪指令DW&#xff08;16位&#xff09;2.1.3双字节伪指令DD2.1.4 多字节定义DF/DQ/DT&#xff08;了解&#xff09; 2.2 常用运算符2.2.1…

SLAM从入门到精通(a*搜路算法)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前机器人常用的搜路算法主要有这么几种&#xff0c;迪杰斯特拉算法、a*算法、贪心算法。每一种算法都有自己的场景和优势&#xff0c;可以灵活选…

工业电子中的深力科分享一款PWM控制器 KA3525A

关于PWM控制器&#xff1a; PWM控制器是一种用于控制电机或其他设备的电路&#xff0c;它通过改变脉冲宽度调制&#xff08;PWM&#xff09;信号的占空比来控制设备的输出。PWM控制器可以使用单片机或开发板等设备来实现&#xff0c;通过设定占空比&#xff0c;可以轻松地控制…

DOS常用命令

1. echo off bat脚本中&#xff0c;该命令可以认为屏蔽掉cmd窗口的盘符 加命令前 加 echo off 之后 2. pause 程序运行完成之后cmd窗口不关闭&#xff0c; 一般程序都是在 1和2之间