1.Linux:jiffies 的源码前后顺序判断
2.ListenableFuture源码解析
3.c++基础语法之future,promise,大全async详细讲解-SurfaceFlinger学习必备c++知识
4.Dubbo调用超时那些事儿
5.Vert.x 源码解析(4.x)——Future源码解析
6.研发必会-异步编程利器之CompletableFuture(含源码 中)
Linux:jiffies 的源码前后顺序判断
jiffies 是Linux基于时间中断的计数器,每次时钟中断会自增1。大全内核中需要判断不同jiffies的源码先后顺序,如超时判断,大全elementui css源码通过转换为将来时间的源码jiffies_future,比较当前jiffies_now是大全否大于等于jiffies_future以判断超时。
jiffies使用无符号整数保存,源码可能溢出,大全如INT_MAX+1变为0,源码需要正确比较不同jiffies大小。大全
内核源码实现两个jiffies大小比较。源码time_after(a,大全 b)判断a是否发生在b后面。((long)(b)-(long)(a)<0)与((long)(a)-(long)(b)>0)等价,源码后者解释原理。
比较大小需考虑几种情况(假设jiffies用位整数存储,a与b差距<公式)。
若a与b差距>公式,上述大小判断失效。例如,a为公式值,b为0时,a虽在b后面发生,但((long)(a)-(long)(b))却<0。
ListenableFuture源码解析
ListenableFuture 是 spring 中对 JDK Future 接口的扩展,主要应用于解决在提交线程池的任务拿到 Future 后在 get 方法调用时会阻塞的问题。通过使用 ListenableFuture,可以向其注册回调函数(监听器),当任务完成时,触发回调。Promise 在 Netty 中也实现了类似的功能,用于处理类似 Future 的场景。
实现 ListenableFuture 的关键在于 FutureTask 的源码解析。FutureTask 是实现 Future 接口的基础类,ListenableFutureTask 在其基础上做了扩展。出售源码程序其主要功能是在任务提交后,当调用 get 方法时能够阻塞当前业务线程,直到任务完成时唤醒。
FutureTask 通过在内部实现一个轻量级的 Treiber stack 数据结构来管理等待任务完成的线程。这个数据结构由 WaitNode 节点组成,每个节点代表一个等待的线程。当业务线程调用 get 方法时,会将自己插入到 WaitNode 栈中,并且在插入的同时让当前线程进入等待状态。在任务执行完成后,会遍历 WaitNode 栈,唤醒等待的线程。
为了确保并发安全,FutureTask 使用 CAS(Compare and Swap)操作来管理 WaitNode 栈。每个新插入的节点都会使用 CAS 操作与栈顶节点进行比较,并在满足条件时更新栈顶。这一过程保证了插入操作的原子性,防止了并发条件下的数据混乱。同时,插入操作与栈顶节点的更新操作相互交织,确保了数据的一致性和完整性。
在 FutureTask 中,还利用了 LockSupport 类提供的 park 和 unpark 方法来实现线程的等待和唤醒。当线程插入到 WaitNode 栈中后,通过 park 方法将线程阻塞;任务执行完成后,通过 unpark 方法唤醒线程,完成等待与唤醒的流程。
综上所述,ListenableFuture 通过扩展 FutureTask 的功能,实现了任务执行与线程等待的高效管理。通过注册监听器并利用 CAS 操作与 LockSupport 方法,实现了在任务完成时通知回调,解决了异步任务执行时的线程阻塞问题,提高了程序的并发处理能力。
c++基础语法之future,极点指标源码promise,async详细讲解-SurfaceFlinger学习必备c++知识
在SurfaceFlinger源码分析中,我遇到了一些新的C++基础语法,比如future和promise。这些工具的引入,使得在多线程环境中访问异步操作的结果变得更加方便。
传统上,在C++中,我们需要通过创建线程并使用`join`等待线程完成,然后将结果赋值给变量。这种过程相对繁琐。为了解决这个问题,C++引入了`std::future`来访问异步操作的结果。`future`类不能立即获取结果,而是在异步操作完成后,通过同步等待或者查询状态来获取结果。`future`的状态有三种:未开始(`future_status::deferred`)、已完成(`future_status::ready`)、超时(`future_status::timeout`)。
`std::async`函数用于创建异步任务,结果保存在`future`对象中。当需要获取异步结果时,通过`future.get()`方法来完成。如果只关注任务完成,可以使用`future.wait()`方法。`async`函数的参数包括线程创建策略(如`std::launch::async`、`std::launch::deferred`)、要执行的函数和函数执行时需要传递的参数。
`std::promise`类帮助线程赋值。在线程函数中,通过`promise`对象的`set_value`方法为外部传递的`promise`赋值。在任务完成后,可以通过`promise`对象关联的`future`获取设置的值。
在实际应用中,`promise`和`future`的源码管理插件结合使得在多线程环境下访问异步操作的结果更加灵活。例如,在SurfaceFlinger源码中,`future`用于等待子线程执行完成,并通过`set_value`设置结果,`get`方法用于获取结果。这种结合使得源码分析更加简便。
为了深入理解这些新语法,我查阅了相关文档,并实践了在SurfaceFlinger源码中的应用。同时,我还使用了性能分析工具如`perfetto`和`systrace`来验证代码的运行效果。这些实践不仅帮助我学习了C++的新语法,还加深了对SurfaceFlinger源码的理解。
如果你对这些C++基础语法感兴趣,或者想要了解SurfaceFlinger源码的详细分析,可以参考我的视频教程,或者私聊我进行深入探讨。我的文章和视频内容涵盖了C++基础语法的学习,以及如何将其应用于实际的SurfaceFlinger源码分析。
Dubbo调用超时那些事儿
其实之前很早就看过Dubbo源码中关于超时这部分的处理逻辑,但是没有记录下来,最近在某脉上看到有人问了这个问题,想着再回顾一下。开始从dubbo的请求开始,看看dubbo(2.6.6)在超时这块是怎么处理的:
com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeChannel#request(java.lang.Object, int)@Overridepublic ResponseFuture request(Object request, int timeout) throws RemotingException { if (closed) { throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");}// create request.Request req = new Request();req.setVersion(Version.getProtocolVersion());req.setTwoWay(true);req.setData(request);DefaultFuture future = new DefaultFuture(channel, req, timeout);try { channel.send(req);} catch (RemotingException e) { future.cancel();throw e;}return future;}DefaultFuture从返回值ResponseFuture类型可以看出,这是一个异步方法(不等同于Dubbo的异步调用)。那么调用超时的关键可以从ResponseFuture来看:
public interface ResponseFuture { Object get() throws RemotingException;Object get(int timeoutInMillis) throws RemotingException;void setCallback(ResponseCallback callback);boolean isDone();}可以看到这是一个接口,从request方法可以得知实现类是DefaultFuture,从构造函数入手:
public DefaultFuture(Channel channel, Request request, int timeout) { this.channel = channel;this.request = request;this.id = request.getId();this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);// put into waiting map.FUTURES.put(id, this);CHANNELS.put(id, channel);}可以得知每一个DefaultFuture都有一个id,并且等于requestId,timeout是从url中获取的配置,没有时默认ms。
从代码的注释可以看到FUTURES这个map应该就是关键,是一个waiting map。
DefaultFuture中还有一个方法:
public static void received(Channel channel,台达源码 Response response) { try { DefaultFuture future = FUTURES.remove(response.getId());if (future != null) { future.doReceived(response);} else { logger.warn("The timeout response finally returned at "+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))+ ", response " + response+ (channel == null ? "" : ", channel: " + channel.getLocalAddress()+ " -> " + channel.getRemoteAddress()));}} finally { CHANNELS.remove(response.getId());}}可以看到调用的地方为:
com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#received
@Overridepublic void received(Channel channel, Object message) throws RemotingException { //省略一些代码} else if (message instanceof Response) { handleResponse(channel, (Response) message);//省略一些代码}}com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#handleResponse
static void handleResponse(Channel channel, Response response) throws RemotingException { if (response != null && !response.isHeartbeat()) { DefaultFuture.received(channel, response);}}回到DefaultFuture.received,可以看到通过Response id从FUTURES中拿了一个DefaultFuture出来,然后调用了doReceived方法,也就是说Response id和Request id 相同。结下来看看doReceived做了什么:
private void doReceived(Response res) { lock.lock();try { response = res;if (done != null) { done.signal();}} finally { lock.unlock();}if (callback != null) { invokeCallback(callback);}}首先是加锁,然后通过唤醒了阻塞在Condition上的线程。看看什么地方会阻塞在done这个条件上:
@Overridepublic Object get(int timeout) throws RemotingException { if (timeout <= 0) { timeout = Constants.DEFAULT_TIMEOUT;}if (!isDone()) { long start = System.currentTimeMillis();lock.lock();try { while (!isDone()) { done.await(timeout, TimeUnit.MILLISECONDS);if (isDone() || System.currentTimeMillis() - start > timeout) { break;}}} catch (InterruptedException e) { throw new RuntimeException(e);} finally { lock.unlock();}if (!isDone()) { throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));}}return returnFromResponse();}是get方法,get方法确实在request请求后被调用:
(Result) currentClient.request(inv, timeout).get()可以看到get方法的大致逻辑为,先获取锁,然后循环判断isDone,并阻塞等到条件,当条件超时,如果任务完成,或者超过timeout结束循环,接着判断isDone,如果超时抛出TimeoutException。并且通过sent(request请求时间)是否>0()来判断是clientSide还是serverSide超时。
isDone逻辑如下:
@Overridepublic boolean isDone() { return response != null;}如果是正常Response,也有可能是超时的现象,可以看到get方法最后调用了一个函数:
public interface ResponseFuture { Object get() throws RemotingException;Object get(int timeoutInMillis) throws RemotingException;void setCallback(ResponseCallback callback);boolean isDone();}0TIMEOUT SIDESERVER_TIMEOUT(服务端超时): 这个就是正常的我们消费端请求一个RPC接口,服务端由于性能等一些原因处理时间超过了timeout配置时间。
CLIENT_TIMEOUT:我们可以看到是通过sent(上面有说sent>0)这个来判断是否clientTimeout,那么这个sent什么时候改变呢?就在发送请求的地方:
public interface ResponseFuture { Object get() throws RemotingException;Object get(int timeoutInMillis) throws RemotingException;void setCallback(ResponseCallback callback);boolean isDone();}1也就是说handler.sent一旦调用成功返回,那么就不算clientSide Timeout了。那么CLIENT_TIMEOUT大概率就是由于client端网络,系统等原因超时。
原文:/post/Vert.x 源码解析(4.x)——Future源码解析
在现代软件开发中,异步编程的重要性日益凸显,提升并发性能并处理大量并行操作。Vert.x,作为一款基于事件驱动和非阻塞设计的异步框架,提供了丰富的工具简化异步编程。本文将深入解析Vert.x 4.x版本的Future源码,理解其关键类和功能。1. 异步核心
Vert.x的核心在于FutureImpl和PromiseImpl,它们是实现异步操作的关键。AsyncResult是通用接口,用于表示异步操作的结果,包含成功值或失败异常。2. Future类详解
Future扩展了AsyncResult,提供了组合操作如join、any、all和map等功能。内部的FutureInternal主要负责添加监听器,FutureBase负责执行监听器和转换函数。 具体来说,FutureImpl的onComplete方法接收一个handler,任务完成后执行,而tryComplete则在异步操作有结果时触发,最终调用用户指定的handler。 相比之下,Promise允许用户手动设置异步结果,PromiseImpl继承自FutureImpl,并增加了context获取功能。3. 实例与源码分析
通过简单的入门实例,如独立使用Future,我们可以看到Vert.x如何通过创建PromiseImpl获取Future。源码分析显示,Promise.future获取Future,OnComplete用于添加监听,而complete方法则用于设置值并通知监听器。4. 深入源码
在源码层面,addListener和emitSuccess方法在OnComplete中扮演重要角色。而complete方法,特别是tryComplete,是设置值并触发监听的关键。5. 总结
总的来说,理解Vert.x中的Future,就是创建PromiseImpl获取Future,通过OnComplete添加监听器,然后通过Promise的complete方法设置值并通知监听器。后续还将深入探讨其他Future实现类,如all、any和map的原理。研发必会-异步编程利器之CompletableFuture(含源码 中)
微信公众号访问地址: 研发必会-异步编程利器之CompletableFuture(含源码 中)
近期热推文章:
1、springBoot对接kafka,批量、并发、异步获取消息,并动态、批量插入库表;
2、SpringBoot用线程池ThreadPoolTaskExecutor异步处理百万级数据;
3、基于Redis的Geo实现附近商铺搜索(含源码)
4、基于Redis实现关注、取关、共同关注及消息推送(含源码)
5、SpringBoot整合多数据源,并支持动态新增与切换(详细教程)
6、基于Redis实现点赞及排行榜功能
7、研发必会-异步编程利器之CompletableFuture(上)
一、多任务组合回调
备注:源码获取方式在文底。
1.1、AND组合关系
thenCombine / thenAcceptBoth / runAfterBoth都表示:将两个CompletableFuture组合起来,只有这两个都正常执行完了,才会执行某个任务。也即:当任务一和任务二都完成再执行任务三(异步任务)。
区别在于:
1、runAfterBoth:不会把执行结果当做方法入参,且没有返回值。
2、thenAcceptBoth:会将两个任务的执行结果作为方法入参,传递到指定方法中,且无返回值。
3、thenCombine:会将两个任务的执行结果作为方法入参,传递到指定方法中,且有返回值。
代码案例:
运行结果:
1.2、OR组合关系
将两个CompletableFuture组合起来,只要其中一个执行完了,就会执行某个任务。(两个任务,只要有一个任务完成,就执行任务三)
区别在于:
1、runAfterEither:不会把执行结果当做方法入参,且没有返回值。
2、acceptEither: 会将已经执行完成的任务,作为方法入参,传递到指定方法中,且无返回值。
3、applyToEither:会将已经执行完成的任务,作为方法入参,传递到指定方法中,且有返回值。(个人推荐)
参考代码:
返回结果:
若将异步任务中的Thread.sleep()改为,将输出的结果为:
从结果中不难对比发现,任务的参数是任务和任务中执行最快的返回结果。
注意:若把核心线程数量改为1,会是什么样的呢?
运行结果:
从上面看出,改为1就变成单线程执行了。
1.3、多任务组合(allOf\anyOf)
1.allOf:等待所有任务都执行完成后,才会执行 allOf 返回的CompletableFuture。如果任意一个任务异常,allOf的CompletableFuture,执行get方法,会抛出异常。(等待所有任务完成才会执行)
2.anyOf:任意一个任务执行完,就执行anyOf返回的CompletableFuture。如果执行的任务异常,anyOf的CompletableFuture,执行get方法,会抛出异常。(只要有一个任务完成)
参考案例:
结果返回:
从结果中看出:等待所有任务都执行完成后,才会执行 allOf 返回的CompletableFuture。
同理anyOf,只需要调整代码:
运行结果:
1.4、thenCompose
thenCompose方法会在某个任务执行完成后,将该任务的执行结果,作为方法入参,去执行指定的方法。该方法会返回一个新的CompletableFuture实例。
1、如果该CompletableFuture实例的result不为null,则返回一个基于该result新的CompletableFuture实例;
2、如果该CompletableFuture实例为null,然后就执行这个新任务。
代码案例:
结果:
二、使用注意点
CompletableFuture 使异步编程更加便利的、代码更加优雅的同时,也要关注使用的一些注意点。
2.1、Future需要获取返回值,才能获取异常信息
代码案例:
输出结果:
Future需要获取返回值(res.get()),才能获取到异常信息。如果不加 get()/join()方法,看不到异常信息。使用的时候,注意一下,考虑是否加try…catch…或者使用exceptionally方法。
若改成exceptionally方法,无需get或join也可以捕获异常信息:
结果:
2.2、CompletableFuture的get()方法是阻塞的
CompletableFuture的get()方法是阻塞的,如果使用它来获取异步调用的返回值,需要添加超时时间。
推荐使用:
2.3、建议使用自定义线程池,不要使用默认的
CompletableFuture代码中使用了默认的线程池,处理的线程个数是电脑CPU核数-1。在大量请求过来的时候,处理逻辑复杂的话,响应会很慢。一般建议使用自定义线程池,优化线程池配置参数。
参考案例:
但是如果线程池拒绝策略是DiscardPolicy或者DiscardOldestPolicy,当线程池饱和时,会直接丢弃任务,不会抛弃异常。因此建议,CompletableFuture线程池策略最好使用AbortPolicy,然后耗时的异步线程,做好线程池隔离。
说明:
AbortPolicy(默认):直接抛弃
CallerRunsPolicy:用调用者的线程执行任务
DiscardOldestPolicy:抛弃队列中最久的任务
DiscardPolicy:抛弃当前任务。
三、源码获取方式
更多优秀文章,请关注个人微信公众号或搜索“程序猿小杨”查阅。然后回复:源码,可以获取对应的源码,开箱即可使用。
如果大家对相关文章感兴趣,可以关注微信公众号"程序猿小杨",会持续更新优秀文章!欢迎大家 分享、收藏、点赞、在看,您的支持就是我坚持下去的最大动力!谢谢!
参考网站:
blog.csdn.net/ThinkWon/...
mp.weixin.qq.com/s/shjA...