异步与线程阻塞

简介:

应该这样理解它

异步,早期开发人员对它有很多误解,认为不阻塞主线程就是异步,更有认为不阻塞UI就是异步,但异步归根结底和这两个东西关系并不大,异步的出现主要是为了提高线程的利用率,让可用线程更高,而不是一个线程只做一件事,这件事没有完成就不去做下面的事情,这是不正确的,线程应该被解放出来!事实上,你如果学过nodejs的话,对单线程非阻塞应该更清楚一些,它主要通过方法回调来实现异步的,只是在语法上和C#不太一样。

说一下上面提到的误解

误解1:不阻塞主线程

如果不阻塞主线程的话,你只能开个新线程完成这个动作,像一些系统通知,它和主线程的工作流程没有关系,如果开个新线程,与主线程并行执行,这并不是我们说的异步,这只是多线程!它会增加线程的开支,使用不当,会影响系统的吞吐量!

误解2:不阻塞UI

这就更属于胡扯了,对于一个工作流来说,必须要按着1,2,3的顺序去执行,如果是同步代码,它是一个线程从1执行到3,这个线程将一直被占用!如果是异步代码,它在执行到1时,线程被回收到池子,其它人可以使用,当1执行完成后,从线程池里取出一个新的线程继续执行,这叫异步!C#的异步进行友好,使用async,await就可以实现了!

实验:查看有效的线程剩余数

        // <summary>
        /// 线程非阻塞,线程利用率高
        /// 线程await后可以去做其它事
        /// 然后await后面方法结束后再申请新线程执行下面的代码
        /// </summary>
        /// <returns></returns>
        [Route("~/do5")]
        public async Task<string> Do5()
        {
            var sw = new Stopwatch();
            sw.Start();
            await HttpHelper.Get("http://localhost:61699/do1");
            await HttpHelper.Get("http://localhost:61699/do2");
            await HttpHelper.Get("http://localhost:61699/do3");
            await HttpHelper.Get("http://localhost:61699/do4");
            sw.Stop();
            int workerThreads, completionPortThreads;
            ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
            return $"max threads:{workerThreads},completionPortThreads:{completionPortThreads},timer:{sw.ElapsedMilliseconds.ToString()}";
        }

我们来看它的I/O线程剩余,多刷新几次,一直维持在32766和32765之间

而如果使用同步代码,结果就完成不一样了,线程剩余各位可以看下面

线程ID在每个await时是不同的

下面是一个更明显的测试,依次执行多个await,然后获取当前线程的ID,它们在异步环境下,有可能是不同的,因为每次都要从池子里拿新的线程!

            await HttpHelper.Get("http://localhost:61699/do1");
            str.Append($"step1:{Thread.CurrentThread.ManagedThreadId}");
            await HttpHelper.Get("http://localhost:61699/do2");
            str.Append($"step2:{Thread.CurrentThread.ManagedThreadId}");
            await HttpHelper.Get("http://localhost:61699/do3");
            str.Append($"step3:{Thread.CurrentThread.ManagedThreadId}");
            await HttpHelper.Get("http://localhost:61699/do4");
            str.Append($"step4:{Thread.CurrentThread.ManagedThreadId}");

通过这篇文章,我们应该真正理解异步这个概念了吧,记住,异步主要为了提高线程利用率,从而提高系统的吞吐量的,它与并行,主线程阻塞很有直接关系,也不是它所研究的重点,这个大家一定要记住!

本文转自博客园张占岭(仓储大叔)的博客,原文链接:异步与线程阻塞,如需转载请自行联系原博主。

目录
相关文章
|
4月前
|
编解码 数据安全/隐私保护 计算机视觉
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
如何使用OpenCV进行同步和异步操作来打开海康摄像头,并提供了相关的代码示例。
180 1
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
|
9天前
|
缓存 安全 Java
面试中的难题:线程异步执行后如何共享数据?
本文通过一个面试故事,详细讲解了Java中线程内部开启异步操作后如何安全地共享数据。介绍了异步操作的基本概念及常见实现方式(如CompletableFuture、ExecutorService),并重点探讨了volatile关键字、CountDownLatch和CompletableFuture等工具在线程间数据共享中的应用,帮助读者理解线程安全和内存可见性问题。通过这些方法,可以有效解决多线程环境下的数据共享挑战,提升编程效率和代码健壮性。
37 6
|
1月前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
109 17
|
2月前
|
Java API 调度
【JavaEE】——多线程(join阻塞,计算,引用,状态)
【JavaEE】——多线程,join,sleep引起的线程阻塞,多线程提升计算效率,如何获取线程的引用和状态
|
4月前
|
安全 调度 C#
STA模型、同步上下文和多线程、异步调度
【10月更文挑战第19天】本文介绍了 STA 模型、同步上下文和多线程、异步调度的概念及其优缺点。STA 模型适用于单线程环境,确保资源访问的顺序性;同步上下文和多线程提高了程序的并发性和响应性,但增加了复杂性;异步调度提升了程序的响应性和资源利用率,但也带来了编程复杂性和错误处理的挑战。选择合适的模型需根据具体应用场景和需求进行权衡。
|
4月前
|
网络协议 安全 Java
难懂,误点!将多线程技术应用于Python的异步事件循环
难懂,误点!将多线程技术应用于Python的异步事件循环
124 0
|
6月前
|
缓存 Java
异步&线程池 线程池的七大参数 初始化线程的4种方式 【上篇】
这篇文章详细介绍了Java中线程的四种初始化方式,包括继承Thread类、实现Runnable接口、实现Callable接口与FutureTask结合使用,以及使用线程池。同时,还深入探讨了线程池的七大参数及其作用,解释了线程池的运行流程,并列举了四种常见的线程池类型。最后,阐述了在开发中使用线程池的原因,如降低资源消耗、提高响应速度和增强线程的可管理性。
异步&线程池 线程池的七大参数 初始化线程的4种方式 【上篇】
|
6月前
|
Java 数据库
异步&线程池 CompletableFuture 异步编排 实战应用 【终结篇】
这篇文章通过一个电商商品详情页的实战案例,展示了如何使用`CompletableFuture`进行异步编排,以解决在不同数据库表中查询商品信息的问题,并提供了详细的代码实现和遇到问题(如图片未显示)的解决方案。
异步&线程池 CompletableFuture 异步编排 实战应用 【终结篇】
|
5月前
|
设计模式 缓存 Java
谷粒商城笔记+踩坑(14)——异步和线程池
初始化线程的4种方式、线程池详解、异步编排 CompletableFuture
|
6月前
|
Java
异步&线程池 CompletableFuture 异步编排 【下篇】
这篇文章深入探讨了Java中的`CompletableFuture`类,解释了如何创建异步操作、使用计算完成时的回调方法、异常处理、串行化方法、任务组合以及多任务组合的使用方式,并通过代码示例展示了各种场景下的应用。
异步&线程池 CompletableFuture 异步编排 【下篇】