异步fifo,在Java并发编程中

伏羲号

异步fifo,在Java并发编程中?

提示

异步fifo,在Java并发编程中

请带着这些问题继续后文,会很大程度上帮助你更好的理解相关知识点。@pdai

为什么要有线程池?Java是实现和管理线程池有哪些方式? 请简单举例如何使用。为什么很多公司不允许使用Executors去创建线程池? 那么推荐怎么使用呢?ThreadPoolExecutor有哪些核心的配置参数? 请简要说明ThreadPoolExecutor可以创建哪是哪三种线程池呢?当队列满了并且worker的数量达到maxSize的时候,会怎么样?说说ThreadPoolExecutor有哪些RejectedExecutionHandler策略? 默认是什么策略?简要说下线程池的任务执行机制? execute –> addWorker –>runworker (getTask)线程池中任务是如何提交的?线程池中任务是如何关闭的?在配置线程池的时候需要考虑哪些配置因素?如何监控线程池的状态?为什么要有线程池线程池能够对线程进行统一分配,调优和监控:

降低资源消耗(线程无限制地创建,然后使用完毕后销毁)提高响应速度(无须创建线程)提高线程的可管理性ThreadPoolExecutor例子Java是如何实现和管理线程池的?

从JDK 5开始,把工作单元与执行机制分离开来,工作单元包括Runnable和Callable,而执行机制由Executor框架提供。

WorkerThread

SimpleThreadPool

程序中我们创建了固定大小为五个工作线程的线程池。然后分配给线程池十个工作,因为线程池大小为五,它将启动五个工作线程先处理五个工作,其他的工作则处于等待状态,一旦有工作完成,空闲下来工作线程就会捡取等待队列里的其他工作进行执行。

这里是以上程序的输出。

输出表明线程池中至始至终只有五个名为 "pool-1-thread-1" 到 "pool-1-thread-5" 的五个线程,这五个线程不随着工作的完成而消亡,会一直存在,并负责执行分配给线程池的任务,直到线程池消亡。

Executors 类提供了使用了 ThreadPoolExecutor 的简单的 ExecutorService 实现,但是 ThreadPoolExecutor 提供的功能远不止于此。我们可以在创建 ThreadPoolExecutor 实例时指定活动线程的数量,我们也可以限制线程池的大小并且创建我们自己的 RejectedExecutionHandler 实现来处理不能适应工作队列的工作。

这里是我们自定义的 RejectedExecutionHandler 接口的实现。

RejectedExecutionHandlerImpl.java

ThreadPoolExecutor 提供了一些方法,我们可以使用这些方法来查询 executor 的当前状态,线程池大小,活动线程数量以及任务数量。因此我是用来一个监控线程在特定的时间间隔内打印 executor 信息。

MyMonitorThread.java

这里是使用 ThreadPoolExecutor 的线程池实现例子。

WorkerPool.java

注意在初始化 ThreadPoolExecutor 时,我们保持初始池大小为 2,最大池大小为 4 而工作队列大小为 2。因此如果已经有四个正在执行的任务而此时分配来更多任务的话,工作队列将仅仅保留他们(新任务)中的两个,其他的将会被RejectedExecutionHandlerImpl 处理。

上面程序的输出可以证实以上观点。

注意 executor 的活动任务、完成任务以及所有完成任务,这些数量上的变化。我们可以调用 shutdown() 方法来结束所有提交的任务并终止线程池。

ThreadPoolExecutor使用详解其实java线程池的实现原理很简单,说白了就是一个线程集合workerSet和一个阻塞队列workQueue。当用户向线程池提交一个任务(也就是线程)时,线程池会先将任务放入workQueue中。workerSet中的线程会不断的从workQueue中获取线程然后执行。当workQueue中没有任务的时候,worker就会阻塞,直到队列中有任务了就取出来继续执行。

Execute原理当一个任务提交至线程池之后:

线程池首先当前运行的线程数量是否少于corePoolSize。如果是,则创建一个新的工作线程来执行任务。如果都在执行任务,则进入2.判断BlockingQueue是否已经满了,倘若还没有满,则将线程放入BlockingQueue。否则进入3.如果创建一个新的工作线程将使当前运行的线程数量超过maximumPoolSize,则交给RejectedExecutionHandler来处理任务。当ThreadPoolExecutor创建新线程时,通过CAS来更新线程池的状态ctl.

参数

corePoolSize 线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize, 即使有其他空闲线程能够执行新来的任务, 也会继续创建线程;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;如果执行了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程。workQueue 用来保存等待被执行的任务的阻塞队列. 在JDK中提供了如下阻塞队列: 具体可以参考JUC 集合: BlockQueue详解ArrayBlockingQueue: 基于数组结构的有界阻塞队列,按FIFO排序任务;LinkedBlockingQueue: 基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQueue;SynchronousQueue: 一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue;PriorityBlockingQueue: 具有优先级的无界阻塞队列;LinkedBlockingQueue比ArrayBlockingQueue在插入删除节点性能方面更优,但是二者在put(), take()任务的时均需要加锁,SynchronousQueue使用无锁算法,根据节点的状态判断执行,而不需要用到锁,其核心是Transfer.transfer().

maximumPoolSize 线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize;当阻塞队列是无界队列, 则maximumPoolSize则不起作用, 因为无法提交至核心线程池的线程会一直持续地放入workQueue.keepAliveTime 线程空闲时的存活时间,即当线程没有任务执行时,该线程继续存活的时间;默认情况下,该参数只在线程数大于corePoolSize时才有用, 超过这个时间的空闲线程将被终止;unit keepAliveTime的单位threadFactory 创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名。默认为DefaultThreadFactoryhandler 线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:AbortPolicy: 直接抛出异常,默认策略;CallerRunsPolicy: 用调用者所在的线程来执行任务;DiscardOldestPolicy: 丢弃阻塞队列中靠最前的任务,并执行当前任务;DiscardPolicy: 直接丢弃任务;当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。

三种类型newFixedThreadPool

线程池的线程数量达corePoolSize后,即使线程池没有可执行任务时,也不会释放线程。

FixedThreadPool的工作队列为无界队列LinkedBlockingQueue(队列容量为Integer.MAX_VALUE), 这会导致以下问题:

线程池里的线程数量不超过corePoolSize,这导致了maximumPoolSize和keepAliveTime将会是个无用参数由于使用了无界队列, 所以FixedThreadPool永远不会拒绝, 即饱和策略失效newSingleThreadExecutor

初始化的线程池中只有一个线程,如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行.

由于使用了无界队列, 所以SingleThreadPool永远不会拒绝, 即饱和策略失效

newCachedThreadPool

线程池的线程数可达到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue作为阻塞队列; 和newFixedThreadPool创建的线程池不同,newCachedThreadPool在没有任务执行时,当线程的空闲时间超过keepAliveTime,会自动释放线程资源,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销; 执行过程与前两种稍微不同:

主线程调用SynchronousQueue的offer()方法放入task, 倘若此时线程池中有空闲的线程尝试读取 SynchronousQueue的task, 即调用了SynchronousQueue的poll(), 那么主线程将该task交给空闲线程. 否则执行(2)当线程池为空或者没有空闲的线程, 则创建新的线程执行任务.执行完任务的线程倘若在60s内仍空闲, 则会被终止. 因此长时间空闲的CachedThreadPool不会持有任何线程资源.关闭线程池遍历线程池中的所有线程,然后逐个调用线程的interrupt方法来中断线程.

关闭方式 - shutdown将线程池里的线程状态设置成SHUTDOWN状态, 然后中断所有没有正在执行任务的线程.

关闭方式 - shutdownNow将线程池里的线程状态设置成STOP状态, 然后停止所有正在执行或暂停任务的线程. 只要调用这两个关闭方法中的任意一个, isShutDown() 返回true. 当所有任务都成功关闭了, isTerminated()返回true.

ThreadPoolExecutor源码详解几个关键属性

内部状态

其中AtomicInteger变量ctl的功能非常强大: 利用低29位表示线程池中线程数,通过高3位表示线程池的运行状态:

RUNNING: -1 << COUNT_BITS,即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;SHUTDOWN: 0 << COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;STOP : 1 << COUNT_BITS,即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;TIDYING : 2 << COUNT_BITS,即高3位为010, 所有的任务都已经终止;TERMINATED: 3 << COUNT_BITS,即高3位为011, terminated()方法已经执行完成

任务的执行execute –> addWorker –>runworker (getTask)

线程池的工作线程通过Woker类实现,在ReentrantLock锁的保证下,把Woker实例插入到HashSet后,并启动Woker中的线程。 从Woker类的构造方法实现可以发现: 线程工厂在创建线程thread时,将Woker实例本身this作为参数传入,当执行start方法启动线程thread时,本质是执行了Worker的runWorker方法。 firstTask执行完成之后,通过getTask方法从阻塞队列中获取等待的任务,如果队列中没有任务,getTask方法会被阻塞并挂起,不会占用cpu资源;

execute()方法ThreadPoolExecutor.execute(task)实现了Executor.execute(task)

为什么需要double check线程池的状态?在多线程环境下,线程池的状态时刻在变化,而ctl.get()是非原子操作,很有可能刚获取了线程池状态后线程池状态就改变了。判断是否将command加入workque是线程池之前的状态。倘若没有double check,万一线程池处于非running状态(在多线程环境下很有可能发生),那么command永远不会执行。

addWorker方法从方法execute的实现可以看出: addWorker主要负责创建新的线程并执行任务 线程池创建新线程执行任务时,需要 获取全局锁:

Worker类的runworker方法

继承了AQS类,可以方便的实现工作线程的中止操作;实现了Runnable接口,可以将自身作为一个任务在工作线程中执行;当前提交的任务firstTask作为参数传入Worker的构造方法;一些属性还有构造方法:

runWorker方法是线程池的核心:

线程启动之后,通过unlock方法释放锁,设置AQS的state为0,表示运行可中断;Worker执行firstTask或从workQueue中获取任务:进行加锁操作,保证thread不被其他线程中断(除非线程池被中断)检查线程池状态,倘若线程池处于中断状态,当前线程将中断。执行beforeExecute执行任务的run方法执行afterExecute方法解锁操作通过getTask方法从阻塞队列中获取等待的任务,如果队列中没有任务,getTask方法会被阻塞并挂起,不会占用cpu资源;

getTask方法

下面来看一下getTask()方法,这里面涉及到keepAliveTime的使用,从这个方法我们可以看出线程池是怎么让超过corePoolSize的那部分worker销毁的。

注意这里一段代码是keepAliveTime起作用的关键:

allowCoreThreadTimeOut为false,线程即使空闲也不会被销毁;倘若为ture,在keepAliveTime内仍空闲则会被销毁。

如果线程允许空闲等待而不被销毁timed == false,workQueue.take任务: 如果阻塞队列为空,当前线程会被挂起等待;当队列中有任务加入时,线程被唤醒,take方法返回任务,并执行;

如果线程不允许无休止空闲timed == true, workQueue.poll任务: 如果在keepAliveTime时间内,阻塞队列还是没有任务,则返回null;

任务的提交

submit任务,等待线程池execute执行FutureTask类的get方法时,会把主线程封装成WaitNode节点并保存在waiters链表中, 并阻塞等待运行结果;FutureTask任务执行完成后,通过UNSAFE设置waiters相应的waitNode为null,并通过LockSupport类unpark方法唤醒主线程;

在实际业务场景中,Future和Callable基本是成对出现的,Callable负责产生结果,Future负责获取结果。

Callable接口类似于Runnable,只是Runnable没有返回值。Callable任务除了返回正常结果之外,如果发生异常,该异常也会被返回,即Future可以拿到异步执行任务各种结果;Future.get方法会导致主线程阻塞,直到Callable任务执行完成;submit方法AbstractExecutorService.submit()实现了ExecutorService.submit() 可以获取执行完的返回值, 而ThreadPoolExecutor 是AbstractExecutorService.submit()的子类,所以submit方法也是ThreadPoolExecutor`的方法。

通过submit方法提交的Callable任务会被封装成了一个FutureTask对象。通过Executor.execute方法提交FutureTask到线程池中等待被执行,最终执行的是FutureTask的run方法;

FutureTask对象public class FutureTask<V> implements RunnableFuture<V> 可以将FutureTask提交至线程池中等待被执行(通过FutureTask的run方法来执行)

内部状态

内部状态的修改通过sun.misc.Unsafe修改

get方法

内部通过awaitDone方法对主线程进行阻塞,具体实现如下:

如果主线程被中断,则抛出中断异常;

判断FutureTask当前的state,如果大于COMPLETING,说明任务已经执行完成,则直接返回;如果当前state等于COMPLETING,说明任务已经执行完,这时主线程只需通过yield方法让出cpu资源,等待state变成NORMAL;通过WaitNode类封装当前线程,并通过UNSAFE添加到waiters链表;最终通过LockSupport的park或parkNanos挂起线程;run方法

FutureTask.run方法是在线程池中被执行的,而非主线程

通过执行Callable任务的call方法;如果call执行成功,则通过set方法保存结果;如果call执行有异常,则通过setException保存异常;任务的关闭shutdown方法会将线程池的状态设置为SHUTDOWN,线程池进入这个状态后,就拒绝再接受任务,然后会将剩余的任务全部执行完

shutdownNow做的比较绝,它先将线程池状态设置为STOP,然后拒绝所有提交的任务。最后中断左右正在运行中的worker,然后清空任务队列。

更深入理解

为什么线程池不允许使用Executors去创建? 推荐方式是什么?线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:

newFixedThreadPool和newSingleThreadExecutor:   主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。newCachedThreadPool和newScheduledThreadPool:   主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。推荐方式 1首先引入:commons-lang3包

推荐方式 2首先引入:com.google.guava包

推荐方式 3spring配置线程池方式:自定义线程工厂bean需要实现ThreadFactory,可参考该接口的其它默认实现类,使用方式直接注入bean调用execute(Runnable task)方法即可

配置线程池需要考虑因素

从任务的优先级,任务的执行时间长短,任务的性质(CPU密集/ IO密集),任务的依赖关系这四个角度来分析。并且近可能地使用有界的工作队列。

性质不同的任务可用使用不同规模的线程池分开处理:

CPU密集型: 尽可能少的线程,Ncpu+1IO密集型: 尽可能多的线程, Ncpu*2,比如数据库连接池混合型: CPU密集型的任务与IO密集型任务的执行时间差别较小,拆分为两个线程池;否则没有必要拆分。监控线程池的状态可以使用ThreadPoolExecutor以下方法:

getTaskCount() Returns the approximate total number of tasks that have ever been scheduled for execution.getCompletedTaskCount() Returns the approximate total number of tasks that have completed execution. 返回结果少于getTaskCount()。getLargestPoolSize() Returns the largest number of threads that have ever simultaneously been in the pool. 返回结果小于等于maximumPoolSizegetPoolSize() Returns the current number of threads in the pool.getActiveCount() Returns the approximate number of threads that are actively executing tasks.参考文章《Java并发编程艺术》https://www.jianshu.com/p/87bff5cc8d8chttps://blog.csdn.net/programmer_at/article/details/79799267https://blog.csdn.net/u013332124/article/details/79587436https://www.journaldev.com/1069/threadpoolexecutor-java-thread-pool-example-executorservice

由于问答代码块插入受限,部分代码未完全展示,若有需要可阅读原文:戳我阅读原文

cyusb配置?

CYPRESS公司推出的2款USB控制器芯片已经成为了市场的主流,被广泛应用于各个行业和领域,它们分别是USB2.0控制器芯片---EZ-USB FX2LP/CY7C68013A 和USB3.0控制器芯片EZ-USB FX3/CYUSB3014。

目前市场上大多数USB2.0 工业相机和USB3.0工业相机都是基于这两款芯片开发。CY7C68013A芯片的内部主要包括高性能微处理器内核、USB2.0收发器、智能引擎(SIE)、增强8051内核、16K的RAM,4K的FIFO、IO接口、数据总线、地址总线,I2C主控制器和通用可编程接口等。实测最高IN传输速度可达50MB/S,无论是接口还是速度都非常适合USB2.0工业相机或者其他USB2.0视频采集的开发。CYUSB3014 是新一代 USB 3.0 外设控制器, 具有一个可进行完全配置的并行通用可编程接口GPIF II,最大位宽32位,频率100MHZ,它可与任何处理器、ASIC 或 FPGA 连接。这个通用可编程接口 GPIF II 是CYPRESS USB 2.0 产品 CY7C68013A中的GPIF 的增强版本。它可轻松无缝地连接至多种常用接口,比如异步 SRAM、异步和同步地址数据复用式接口、并行 ATA 等等。CYUSB3014 带有运行频率为200MHZ的ARM926EJ内核,512K 嵌入式SRAM。具有1MHZ频率的I2C主控制器,33MHZ的SPI主控制器。实测在PC USB3.0接口IN传输速度高达400MB/S,如果算上外设整个系统的传输速度也可达320多MB/S。那么基于CYUSB3014开发的USB3.0工业相机与CY7C68013A开发的USB2.0工业相机相比有哪些优势呢?可见,基于CYUSB3014开发的USB3.0工业相机会比基于CY7C68013A开发的USB2.0工业相机具有更高的速度,更高的帧率,特别是在高象素SENSOR的应用上会有更好的效果,图象更加流畅。而且由于CYUSB3014具有更强的处理能力,使得原来必须放到PC上位机软件中或者FPGA等外加处理器中处理的RGB转YUV,BAYER转RGB24等可以在CYUSB3014内部完成,减少了PC端的CPU利用率,提高了系统集成度。另一方面,由于EZ-USB FX3 内部有比EZ-USB FX2LP更多的RAM,加上传输带宽也高了非常多,这就使得用最简硬件结构(不使用FPGA和外部存储芯片等)开发的USB工业相机也可以有很好的稳定性和很高的实际帧率。再者,CYUSB3014 有着更多的外设控制接口,数据位宽,更多的GPIO,从而也就比CY7C68013A有更多的灵活性,更加适合USB工业相机的周边扩展应用,能与更多的SENSOR 或者其他视频解码芯片等前端进行无缝连接。

计算机组织原理知识点?

一.冯诺依曼体系

1.采用二进制表示信息

2.采用存储程序工作方式

3.计算机硬件系统由:运算器,控制器,存储器,输入设备,输出设备 组成

二.计算机主要性能指标

1.基本字长

2.运算速度 (时钟频率,IPS等)

3.数据通路宽度和数据传输率: 数据传输率 = 总线位数/8*时钟频率

4.存储容量

5.外围设备配置

6.软件配置

三.常见寻址方式

1.立即寻址

2.直接寻址 (A)

3.寄存器寻址 R

4.间接寻址 @

5.寄存器间址 (R) (R)+, -(R)

6.变址寻址 X(R) PC+R

7.基址寻址

8.基址加变址方式

9.相对寻址 X(PC) PC+(PC)

10.页面寻址

11.堆栈寻址

四.CPU基本组成

1.运算部件

2.寄存器组

3.微命令产生部件

4.时序系统

5.内部通路结构

五.主机与外设连接模式

1.辐射型

2.总线型

3.通道型

六.规格化浮点加减运算

1.判零等,看是否能简化。

2.对阶,小阶向大阶对齐,尾数右移

3.尾数相加减

3.结果规格化: |M| > 1 右规 |M| < 1/2 左规

七.CPU信息传送方式

1.直接程序传送方式

2.程序中断传送方式

3.DMA方式

八.存储器分类

(1)按物理存储机制(存储介质)分

1.半导体存储器

<1>静态存储器 :双稳态触发器。 需电源 。 适用做Cache及主存

<2>动态存储器 :电容 。 需动态刷新,因为电荷会泄漏 。 适用做主存

2.磁表面存储器 。 适用做外存

(2)按存储方式分

1.随机存取(RAM) :可按地址随机访问任意存储单元,读写时间与位置无关

2.顺序存取(SAM) :按记录块组织,顺序存放的,访问时间与信息存放位置有关

3.直接存取(DAM) :先将读写部件指向某一区域,再在该区域进行顺序查找,读写时间与位置有关

(3)按读写特性

<1>只读型

<2>一次写入型

<3>可擦除/重写型

九.存储器关键特性

1.存储容量

2.存取时间 TA

3.存取周期 TM

4.数据传输率 DTR = WIDTH/TM (bps)

十.动态存储器刷新

1.集中刷新方式

2.分散刷新方式

3.异步刷新方式 按行数决定所需刷新周期数,并分散在2ms周期中

十一.磁记录方式

1.不归零-1制 (NRZ1) : 写1则翻转

2.调相制 (PM) : 写0在位单元中间产生负跳变,否则正跳变,连续两位相同交界处变向

3.调频制 (FM) : 每次交界处都变向,写0则位单元中间不变,写1位单元中间变向

4.改进型调频制 (M^2F) : 与调频制基本一样,只是只有在0,0交界处变向

5.群码制 (GCR)

十二.磁表面存储器的校验

1.海明校验 :分组进行奇偶校验,码距为d时,可检查出2(d-1)位错 或 检测并纠正1位错。 适用于快速自动纠错

2.循环校验码CRC : 。 适用于位数多,大量数据

<1>将待编码k位有效信息M(x)左移r位,得M(x)*x^r

<2>选取r+1位的生成多项式G(x),做mod2除

M(x)*x^r/G(x) = Q(x) + R(x)/G(x)

<3>mod2加 : M(x)*x^r + R(x) 得出循环校验码

十三.Cache地址映像

1.直接映像

2.全相联映像

3.组相联映像

替换算法: FIFO,LRU

十四.虚拟存储器

1.页式

2.段式

3.段页式,结合上述两种

十五.串行接口与并行接口

1.串行接口: 接口与外部设备串行,接口与系统总线并行,除非指定串行

2.并行接口: 接口与外部设备,系统总线皆并行

十六.向量中断与非向量中断

1.向量中断: 直接依靠硬件来确定中断程序入口地址

2.非向量中断: 执行软件,用查询方式确定入口地址

十七.中断响应过程

关中断 -> 保存断点 -> 获取服务程序入口地址 -> 转向程序运行状态

十八.DMA初始化信息

1.外设寻址信息

2.控制字

3..主存缓冲区首址

4.交换量

十九.总线

总线是指一组能为多个部件分时共享的信息传送线。

二十.组合逻辑控制方式和微程序控制方式

1.组合逻辑控制方式: 微命令由组合逻辑电路产生

优: 速度快

劣: 设计不规整,不易修改或扩展

2.微程序控制方式:

<1>一条微指令由多条微命令组成,控制一步操作

<2>一段微程序由多条微指令组成,执行一条机器指令

<3>微程序存储在控制存储器中,需要时取出执行

优: 结构简化规整,易于修改或扩展,可靠性高

劣: 速度慢

mq与pq有什么区别?

MQ和PQ是两种不同的概念,它们分别代表消息队列(Message Queue)和电力质量指标(Power Quality)。MQ主要是指应用程序对应用程序的消息通信,一端只管往队列不断发布信息,另一端只管往队列中读取消息,发布者不需要关心读取消息的谁,读取消息者不需要关心发布消息的是谁,各干各的互不干扰。目前市场上常用的消息队列有RabbitMQ、RocketMQ、Kafka等。PQ则是电力质量指标的缩写,用于描述电力系统中各种电能参数的统计量,包括电压、电流、频率、相位等参数。电力质量的好坏直接关系到电力系统的安全性、稳定性和可靠性。因此,电力质量指标是评估电力系统运行质量的重要依据。总的来说,MQ和PQ分别用于不同的场景和目的,MQ主要用于应用程序之间的消息通信,而PQ则主要用于评估电力系统的运行质量。

懂得看原理图电路图去做嵌入式?

不知道你工作过程中会碰到什么样的软件问题,对于维修人员而言,估计就是操作一些简单的刷机之类的,这种是操作层面的,不算什么软件,对着视频和手册流程来走就是了,如果是涉及芯片层面的一些改写,你不要汇编知识,是不容易掌握的,除非你下定决心从零开始,请关注:容济点火器

说说我的经历,我以前读的是自动化专业,什么都是半桶水,但是在学校里边学过C语言和一些简单51单片机知识,毕业后进工厂,从电工做起,后来做的售后服务,接触的都是继电器,PLC,变频器这些产品的维修维护,当然都是硬件的了,而这些产品维修,基本上都是电气方面的,远远不如电子维修那么复杂了。

后来做项目,自学了PLC和触摸屏编程,因为有电气电路维修基础,PLC编程一般就是使用梯形图,本质是就是继电器电路的软化版本了,所以学起来感觉并不难,而触摸屏编程也是二次变成,都是图形化的,和硬件电气电路思维比较接近。后来还接触了一些数控类的编程,这些和加工思路是相关的,还是硬件逻辑思路。

再后来接触了变频器,伺服,直流调速器等电子电路维修,也开始钻研了电子电路维修上的一些知识,在这个时间段,也掌握了一些如何看懂电子电路图的一些技巧,基本上从电气到电子,我花了很长时间去升级了自己了,虽然都是电路,但是电子电路要比电气电路复杂很多。我粗略统计了一下,大概花了5年时间。

接着从事了汽车电子这个行业了,在一些朋友帮助下,自己把以前大学的C语言编程知识重新捡了回来,独立完成了8位和32位单片机的编程,这个过程也是很伤脑筋的了,从电子硬件到软件,虽然是有一些思路是相近的,但是本质还是不同的东西了。现在可以说基本掌握了嵌入式类系统的编程了,或者可以读懂别人写的一些程序,但是和专业的软件工程师相比,还是有差距的。

前阵子,又接触了JAVA和XCODE之类的编程,发现这种面向对象的编程和面向过程嵌入式系统编程差异非常大了,还需要一段时间去学习的。

所以建议楼主,在硬件比较厉害的前提下,你可以从C语言开始来学习编程,掌握了C语言,其他那些会相对容易点,毕竟C语言和硬件息息相关,对于你会是比较好的入门了,不过要有心理准备,没有个几年时间,你是很难完成从硬件工程师转换成软件工程师的。

发表评论

快捷回复: 表情:
AddoilApplauseBadlaughBombCoffeeFabulousFacepalmFecesFrownHeyhaInsidiousKeepFightingNoProbPigHeadShockedSinistersmileSlapSocialSweatTolaughWatermelonWittyWowYeahYellowdog
评论列表 (暂无评论,93人围观)

还没有评论,来说两句吧...