多线程和异步编程示例和实践-踩过的坑

上两篇文章,主要介绍了Thread、ThreadPool和TPL

多线程异步编程示例和实践-Thread和ThreadPool

多线程异步编程示例和实践-Task

本文中,分享两则我们在做多线程和异步编程中实际踩过的坑,实际生产环境遇到的问题,以及解决办法。

1. HttpClient

业务场景:使用HttpClient实现第三方业务推送,当第三方的Http服务器不通、或者返回很慢时

  • 线程数暴涨
  • Asp.Net\Asp.Net MVC场景下,并发多线程导致的线程阻塞:HttpClient.PostAysnc/GetAsync.Result

第一种线程数暴涨的问题,很容易理解,线程执行的慢,线程创建的速度快,一直在执行,导致线程不断创建线程、线程数不断暴涨。

根本原因是HttpClient的超时时间配置的太长,这点存于业务设计的问题,理论上简单请求3s没响应就该超时了,复杂长时间请求使用异步任务(我们的消息应用中心)处理。

第二种,Asp.Net或者Asp.Net MVC场景下,使用HttpClient,同时GetAsync.Result,异步变同步,这就会出现请求Hang住,线程死锁、请求没响应。

根本原因Http异步请求的上下文的互相等待导致的。详细分析可以参考下面的link:

http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

有什么好的解决方案吗

  • HttpWebRequest代替HttpClient实现各类Http请求,这个类虽然是.net 2.0下的,但是功能的确很强大,应用一般的Http请求没啥问题!
  • 使用HttpWebRequest实现同步Get、Post请求,避免在Asp.Net场景下使用HttpClient.PostAsync/GetAsync.Result(异步变同步)带来的线程死锁,因为Asp.Net机制下Http异步请求的上下文的互相等待导致的。
  • 合理设置Http请求的超时时间,超时即返回,防止线程数暴涨
  • 对于第三方服务器的通信,建议在内存中维护一个联通性状态字典,每隔一段时间更新一次,如果不通,直接就不发起Http请求了,这个策略更像是设计上的优化,但是效果很好!

上两个示例代码:

如果非用HttpClient不可,也是有招的:

很简单,从WebApi,或者Controller这一层全部异步化。前后全部异步。同时,

设置:ConfigureAwait(false)

2. Winform、Console或者Windows服务下,前台线程异常导致的进程退出

这个问题是很头疼的,主要是在提供的服务容器运行的插件代码中,使用了Thread,默认是前台线程,未正确处理异常,线程异常后,导致主进程退出!!!

业务场景:Windows 服务版本下的消息应用中心,个推使用New Thread推送消息,线程未处理异常,异常后导致Windows Service异常关闭!

结果很头大,就是整个消息应用中心进程退出了,所有消息处理业务全部受影响!

根本原因:通过New Thread方式创建的线程,系统默认都是前台线程(IsBack),前台线程异常如果未处理,会导致应用程序退出,即使注册了AppDomain.CurrentDomain.UnhandledException事件处理,应用程序依然会挂掉。

解决方案:在线程内部合理处理异常:记日志、返回值处理,但是不能throw

补充:这里引出了一个重要的知识点:前台线程和后台线程

两者的区别就是:

  • 应用程序必须运行完所有的前台线程才可以退出;
  • 对于后台线程,应用程序则可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束。

那么,Thread、线程池的线程、Task都各自属于哪一类线程?

使用Thread建立的线程默认情况下是前台线程,即线程属性IsBackground=false.

属于托管线程池的线程(即其 IsThreadPoolThread 属性为 true 的线程)是后台线程

Task都是后台线程:Task需要在其运行过程中至少有一个前台线程在跑,否则会直接退出.

周国庆

2017/6/16

时间: 2024-08-04 14:16:04

多线程和异步编程示例和实践-踩过的坑的相关文章

多线程异步编程示例和实践-Thread和ThreadPool

说到多线程异步编程,总会说起Thread.ThreadPool.Task.TPL这一系列的技术.总结整理了一版编程示例和实践,分享给大家. 先从Thread和ThreadPool说起: 1. 创建并启动线程 2. 暂停线程 当前线程在执行Thread.Sleep方法时,会等待指定的时间(1000ms)此时,当前线程处于阻塞状态:WaitSleepJoin 3. 线程等待 当程序运行时,启动了一个耗时较长的线程打印数字,每次打印输出前需要等待1000ms,我们在主程序中调用ThreadJoin方法

初步谈谈 C# 多线程、异步编程与并发服务器

多线程与异步编程可以达到避免调用线程异步阻塞作用,但是两者还是有点不同. 多线程与异步编程的异同: 1.线程是cpu 调度资源和分配的基本单位,本质上是进程中的一段并发执行的代码. 2.线程编程的思维符合正常人的思维习惯,线程中的处理程序依然是顺序执行,所以编程起来比较方便,但是缺点也是明显的,多线程的使用会造成多线程之间的上下文切换带来系统花销,并且共享变量之间也是会造成死锁的问题. 3.因为异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变

多线程之异步编程: 经典和最新的异步编程模型,async与await

经典的异步编程模型(IAsyncResult) 最新的异步编程模型(async 和 await) 将 IAsyncInfo 转换成 Task 将 Task 转换成 IAsyncInfo 示例1.使用经典的异步编程模型(IAsyncResult)实现一个支持异步操作的类Thread/Async/ClassicAsync.cs /* * 使用经典的异步编程模型(IAsyncResult)实现一个支持异步操作的类 */ using System; using System.Collections.Ge

C# - 多线程之 异步编程

异步编程 同步编程,请求响应模型,同步化.顺序化.事务化. 异步编程,事件驱动模型,以 Fire and Forget 方式实现. 异步编程模式 ?-§-?异步编程模型 (APM) 模式: IAsyncResult 模式,异步操作需要 Begin 和 End 方法: ?-§-?基于事件的异步模式(EAP):事件.事件处理程序委托类型和 EventArg 派生类型: ?-§-?基于任务的异步模式(TAP):推荐模式,.NET Framework 4 引入,基于 System.Threading.T

C#多线程之异步编程

c#中异步编程,主要有两种方法: 1.委托的异步调用: 2.Task的await,async (c# 4.5) 我们来看例子: 1 /// <summary> 2 /// 异步保存网页,url:网页地址,path:要保存的位置 3 /// </summary> 4 private void SavePageAsync(string url, string path) 5 { 6 Func<string, string, bool> fun = SavePageSingl

多线程Java Socket编程示例(转)

这篇做为学习孙卫琴<<Java网络编程精解>>的学习笔记吧.其中采用Java 5的ExecutorService来进行线程池的方式实现多线程,模拟客户端多用户向同一服务器端发送请求. 1.服务端 package sterning; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import

多线程Java Socket编程示例

package org.merit.test.socket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; public class Hand

C#多线程与异步

多线程与异步编程,一直是小白变成(●—●)的一个坎.平时也用到过不少多线程与异步操作,在这里进行一下记录. 异步与多线程的概念 平时可能会遇见需要爬网页数据,这个时候如果数据很庞大.那么单线程会造成等待.如果这个时候使用Thread开一个线程,经管会避免UI线程卡顿,但是依然会很慢.这就是典型的多线程错误使用情况. 那么针对此情景,需要进行如何处理呢?首先要理解一下,网页数据的操作,IO操作. IO操作:IO流实际上是一种无结构的字节序或字符序列,进行插入和移除就是IO操作.均是以IO流为基础,

Async await 异步编程说明

希望在编程上有些许提高所以 最近连续2篇博客都在说明多线程和异步编程的使用,异步和多线程之间区别请自行百度,因为理解不是特别透彻就不在叙述以免误导大家,这里写下新研究整理 task  和 await 的异步编程使用 调用子方法和耗时方法如下 /// <summary> /// 有返回值异步方法 /// </summary> /// <returns></returns> static async Task<int> HaveReturnAsync