问题现场

在多线程环境下使用HttpClient组件对某个HTTP服务发起请求,运行一段时间之后发现客户端主机CPU利用率呈现出下降趋势,而不是一个稳定的状态。

而且,从程序日志中判断有线程处于hang住的状态,应该是被阻塞了。

问题排查

一开始找不到原因,怀疑是多线程并发导致的死锁问题,但是通过代码审查并未定位到任何可能的多线程并发问题。

甚至开始怀疑是否是因为内存资源不够引起JVM频繁GC到导致业务线程被暂停,但是从GC的日志输出结果看,GC是正常的。

于是,进入一种丈二和尚摸不着头脑头脑的状态,再次Review代码,发现并未设置请求超时时间,于是设置超时控制,发现问题依然存在,彻底懵逼了。

最后,dump线程堆栈和内存堆栈,再对堆栈数据进行分析。从分析结果看,确认是因为Socket连接在读取数据时被阻塞引起线程hang住。搜索“httpclient 超时”关键字,找到各式各样设置HttpClient超时控制的方式,均尝试过但是并未生效。

实际上到后来才知道,HttpCient的超时控制在不同的版本中设置请求超时参数的方式均各不相同,这才导致了我使用了网上看到的方式设置之后并未生效。当然,根本原因还是因为对HttpClient这个组件不熟悉导致的.

问题重现

1.HttpClient版本

  1. <dependency>
  2. <groupId>org.apache.httpcomponents</groupId>
  3. <artifactId>httpclient</artifactId>
  4. <version>4.5.2</version>
  5. </dependency>

2.Java代码

  1. public class HttpClientTest {
  2. private AtomicInteger counter = new AtomicInteger(0);
  3. private String url = "http://www.baidu.com/";
  4. public static void main(String[] args) {
  5. new HttpClientTest().test();
  6. }
  7. // 执行测试
  8. private void test() {
  9. int number = 100000; // 总请求数
  10. int concurrent = 50; // 每次并发请求数
  11. CountDownLatch countDownLatch = new CountDownLatch(number); // 计数器
  12. ExecutorService threadPool = Executors.newFixedThreadPool(concurrent); // 线程池
  13. int concurrentPer = concurrent;
  14. boolean over = false;
  15. while(!over) {
  16. number = number - concurrent;
  17. if(number <= 0) {
  18. concurrentPer = number + concurrent;
  19. over = true;
  20. }
  21. // 线程池批量提交
  22. for(int i = 0; i < concurrentPer; i++) {
  23. threadPool.execute(new Runnable() {
  24. @Override
  25. public void run() {
  26. try {
  27. request(url);
  28. Thread.sleep(100);
  29. } catch (IOException | InterruptedException e) {
  30. e.printStackTrace();
  31. } finally {
  32. countDownLatch.countDown();
  33. }
  34. }
  35. });
  36. }
  37. }
  38. try {
  39. countDownLatch.await();
  40. threadPool.shutdown();
  41. } catch (InterruptedException e) {
  42. e.printStackTrace();
  43. }
  44. }
  45. // 访问指定地址
  46. private void request(String url) throws IOException {
  47. HttpGet httpGet = new HttpGet(url);
  48. commnicate(httpGet);
  49. }
  50. // 负责底层通信处理
  51. private void commnicate(HttpRequestBase request) throws IOException {
  52. ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
  53. @Override
  54. public String handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
  55. return EntityUtils.toString(response.getEntity());
  56. }
  57. };
  58. HttpClient client = HttpClients.createDefault();
  59. String body = client.execute(request, responseHandler); // 线程可能会在这里被阻塞
  60. System.out.println(String.format("body size: %s, counter: %s", body.length(), counter.incrementAndGet()));
  61. }
  62. }

运行上述代码一段时间后很容易可以重现出问题,如下为运行控制台信息:

并且线程全部hang住,进程无法正常结束.

查看端口状态存在大量请求处于建立连接状态(ESTABLISHED):

  1. # netstat -anpt
  2. tcp 0 0 172.17.7.81:56408 180.118.128.179:56033 ESTABLISHED 3766/java
  3. tcp 0 0 172.17.7.81:57644 115.202.238.177:27016 ESTABLISHED 3847/java
  4. tcp 0 0 172.17.7.81:36616 117.57.21.0:25719 ESTABLISHED 3766/java
  5. tcp 0 0 172.17.7.81:59944 112.245.197.118:57220 ESTABLISHED 3847/java
  6. tcp 0 0 172.17.7.81:48722 218.5.215.10:40835 ESTABLISHED 4007/java
  7. tcp 0 0 172.17.7.81:52734 115.194.17.14:45210 ESTABLISHED 4007/java
  8. tcp 0 0 172.17.7.81:60586 59.32.37.129:16637 ESTABLISHED 3686/java
  9. tcp 0 0 172.17.7.81:36776 222.89.86.109:21667 ESTABLISHED 3766/java
  10. tcp 0 0 172.17.7.81:51690 60.161.249.162:59039 ESTABLISHED 3927/java
  11. tcp 0 0 172.17.7.81:42226 58.218.200.59:80 TIME_WAIT -
  12. tcp 0 0 172.17.7.81:56566 117.70.47.194:40879 ESTABLISHED 3686/java
  13. tcp 0 0 172.17.7.81:43266 182.120.202.204:45893 ESTABLISHED 3766/java
  14. tcp 0 0 172.17.7.81:55630 60.169.223.16:21280 ESTABLISHED 3927/java
  15. tcp 0 0 172.17.7.81:54922 60.168.81.26:25464 ESTABLISHED 3927/java
  16. tcp 0 0 172.17.7.81:53352 112.252.97.83:53584 ESTABLISHED 3847/java
  17. tcp 0 0 172.17.7.81:52684 113.121.242.43:14447 ESTABLISHED 3927/java
  18. tcp 0 0 172.17.7.81:54750 113.121.241.168:45173 ESTABLISHED 3686/java
  19. tcp 0 0 172.17.7.81:41510 113.105.202.106:47288 ESTABLISHED 4007/java
  20. tcp 0 0 172.17.7.81:38804 121.232.148.62:57938 ESTABLISHED 3847/java
  21. tcp 0 0 172.17.7.81:41468 113.105.202.106:47288 ESTABLISHED 3927/java
  22. tcp 0 0 172.17.7.81:45444 123.163.81.185:22012 ESTABLISHED 3766/java
  23. tcp 0 0 172.17.7.81:54810 113.121.241.168:45173 ESTABLISHED 4007/java
  24. tcp 0 0 172.17.7.81:51542 175.153.23.147:20766 ESTABLISHED 3927/java
  25. tcp 0 0 172.17.7.81:45644 218.5.215.10:40835 ESTABLISHED 4007/java
  26. tcp 0 0 172.17.7.81:35730 116.53.197.198:30042 ESTABLISHED 3766/java
  27. tcp 0 0 172.17.7.81:54738 113.121.241.168:45173 ESTABLISHED 3686/java
  28. tcp 0 0 172.17.7.81:60600 59.32.37.129:16637 ESTABLISHED 3686/java
  29. tcp 0 0 172.17.7.81:54862 113.121.241.168:45173 ESTABLISHED 4007/java
  30. tcp 0 0 172.17.7.81:40980 115.225.153.215:17292 ESTABLISHED 3686/java
  31. tcp 0 0 172.17.7.81:54166 123.149.162.129:18269 ESTABLISHED 3766/java
  32. tcp 0 0 172.17.7.81:60712 120.35.190.184:33054 ESTABLISHED 3766/java
  33. tcp 0 0 172.17.7.81:55802 106.42.211.65:59547 ESTABLISHED 3766/java

同时,分析线程堆栈信息(jstack -F -l pid > thread_stack.log)可以看到如下信息:

  1. "pool-1-thread-45" #55 prio=5 os_prio=0 tid=0x00007f78702df000 nid=0x33d5 runnable [0x00007f7830c1d000]
  2. java.lang.Thread.State: RUNNABLE
  3. at java.net.SocketInputStream.socketRead0(Native Method)
  4. at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
  5. at java.net.SocketInputStream.read(SocketInputStream.java:171)
  6. at java.net.SocketInputStream.read(SocketInputStream.java:141)
  7. at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:139)
  8. at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:155)
  9. at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:284)
  10. at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:140)
  11. at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:57)
  12. at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:261)
  13. at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:165)
  14. at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:167)
  15. at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:272)
  16. at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:124)
  17. at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:271)
  18. at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
  19. at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
  20. at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
  21. at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
  22. at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:71)
  23. at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:220)
  24. at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:164)
  25. at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:139)
  26. at org.chench.extra.HttpClientTest.commnicate(HttpClientTest.java:106) # 线程在这里阻塞
  27. at org.chench.extra.HttpClientTest.request(HttpClientTest.java:93)
  28. at org.chench.extra.HttpClientTest.access$100(HttpClientTest.java:31)
  29. at org.chench.extra.HttpClientTest$1.run(HttpClientTest.java:62)
  30. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  31. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  32. at java.lang.Thread.run(Thread.java:748)
  33. Locked ownable synchronizers:
  34. - <0x0000000086d50638> (a java.util.concurrent.ThreadPoolExecutor$Worker)

从堆栈日志中可以看到,线程处于RUNNABLE状态,并且阻塞在at org.chench.extra.HttpClientTest.commnicate(HttpClientTest.java:106)处.

解决方案

通过线程堆栈日志分析可以定位到线程hang住是因为HttpClient在执行访问时被阻塞了,结合源代码找到阻塞原因是因为未设置请求超时时间.

上述问题本质上是因为HttpClient组件并未设置请求超时控制导致的:虽然连接超时,但是读取失败,导致线程一直被阻塞.

那么,应该如何设置HttpClient的超时时间呢?鉴于HttpClient的官方文档没有明确说明,并且不同版本的HttpClient组件设置超时控制的方式不一致,所以建议直接查看源码.

HttpClient执行访问请求时序图如下:

顺藤摸瓜,在MainClientExec.javaexecute()方法中看到有2处使用了timeout参数,其含义各不相同:

(1)在获取HttpClientConnection对象时需要读取配置参数中的ConnectionRequestTimeout值,该参数用于控制获取连接的超时时间.



(2)获取到HttpClientConnection对象之后读取配置参数中的SocketTimeout值,设置Socket超时时间.



显然,这2个timeout参数都需要从RequestConfig对象中获取.

既然找到了使用timeout参数的地方,下一步需要确定该参数是如何设置的.沿着HttpClient的请求时序图路径往回查找,在InternalHttpClient.java类的doExecute()方法中可以很清晰地看到设置了RequestConfig对象参数.

  1. @Override
  2. protected CloseableHttpResponse doExecute(
  3. final HttpHost target,
  4. final HttpRequest request,
  5. final HttpContext context) throws IOException, ClientProtocolException {
  6. Args.notNull(request, "HTTP request");
  7. HttpExecutionAware execAware = null;
  8. if (request instanceof HttpExecutionAware) {
  9. execAware = (HttpExecutionAware) request;
  10. }
  11. try {
  12. final HttpRequestWrapper wrapper = HttpRequestWrapper.wrap(request, target);
  13. final HttpClientContext localcontext = HttpClientContext.adapt(
  14. context != null ? context : new BasicHttpContext());
  15. // 通过RequestConfig对象配置连接参数
  16. RequestConfig config = null;
  17. if (request instanceof Configurable) {
  18. // 如果在HttpRequest对象中设置了RequestConfig属性,直接使用
  19. config = ((Configurable) request).getConfig();
  20. }
  21. if (config == null) {
  22. // 如果在HttpRequest对象中未设置RequestConfig对象属性,则获取HttpParams属性构造RequestConfig对象
  23. final HttpParams params = request.getParams();
  24. if (params instanceof HttpParamsNames) {
  25. if (!((HttpParamsNames) params).getNames().isEmpty()) {
  26. config = HttpClientParamConfig.getRequestConfig(params);
  27. }
  28. } else {
  29. config = HttpClientParamConfig.getRequestConfig(params);
  30. }
  31. }
  32. if (config != null) {
  33. // 使用RequestConfig对象配置连接参数
  34. localcontext.setRequestConfig(config);
  35. }
  36. setupContext(localcontext);
  37. final HttpRoute route = determineRoute(target, wrapper, localcontext);
  38. return this.execChain.execute(route, wrapper, localcontext, execAware);
  39. } catch (final HttpException httpException) {
  40. throw new ClientProtocolException(httpException);
  41. }
  42. }

(3)HttpClient默认使用的连接池为PoolingHttpClientConnectionManager,在建立连接时(connect()方法)会使用其中的SocketConfig配置参数对Socket进行配置,如下所示:

  • PoolingHttpClientConnectionManager.java
  1. @Override
  2. public void connect(
  3. final HttpClientConnection managedConn,
  4. final HttpRoute route,
  5. final int connectTimeout,
  6. final HttpContext context) throws IOException {
  7. Args.notNull(managedConn, "Managed Connection");
  8. Args.notNull(route, "HTTP route");
  9. final ManagedHttpClientConnection conn;
  10. synchronized (managedConn) {
  11. final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn);
  12. conn = entry.getConnection();
  13. }
  14. final HttpHost host;
  15. if (route.getProxyHost() != null) {
  16. host = route.getProxyHost();
  17. } else {
  18. host = route.getTargetHost();
  19. }
  20. final InetSocketAddress localAddress = route.getLocalSocketAddress();
  21. SocketConfig socketConfig = this.configData.getSocketConfig(host);
  22. if (socketConfig == null) {
  23. // 使用配置参数SocketConfig
  24. socketConfig = this.configData.getDefaultSocketConfig();
  25. }
  26. if (socketConfig == null) {
  27. socketConfig = SocketConfig.DEFAULT;
  28. }
  29. this.connectionOperator.connect(
  30. conn, host, localAddress, connectTimeout, socketConfig, context);
  31. }
  • DefaultHttpClientConnectionOperator.java
  1. @Override
  2. public void connect(
  3. final ManagedHttpClientConnection conn,
  4. final HttpHost host,
  5. final InetSocketAddress localAddress,
  6. final int connectTimeout,
  7. final SocketConfig socketConfig,
  8. final HttpContext context) throws IOException {
  9. final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context);
  10. final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());
  11. if (sf == null) {
  12. throw new UnsupportedSchemeException(host.getSchemeName() +
  13. " protocol is not supported");
  14. }
  15. final InetAddress[] addresses = host.getAddress() != null ?
  16. new InetAddress[] { host.getAddress() } : this.dnsResolver.resolve(host.getHostName());
  17. final int port = this.schemePortResolver.resolve(host);
  18. for (int i = 0; i < addresses.length; i++) {
  19. final InetAddress address = addresses[i];
  20. final boolean last = i == addresses.length - 1;
  21. Socket sock = sf.createSocket(context);
  22. // 使用socketConfig参数中的超时时间对Socket进行配置
  23. sock.setSoTimeout(socketConfig.getSoTimeout());
  24. sock.setReuseAddress(socketConfig.isSoReuseAddress());
  25. sock.setTcpNoDelay(socketConfig.isTcpNoDelay());
  26. sock.setKeepAlive(socketConfig.isSoKeepAlive());
  27. if (socketConfig.getRcvBufSize() > 0) {
  28. sock.setReceiveBufferSize(socketConfig.getRcvBufSize());
  29. }
  30. if (socketConfig.getSndBufSize() > 0) {
  31. sock.setSendBufferSize(socketConfig.getSndBufSize());
  32. }
  33. final int linger = socketConfig.getSoLinger();
  34. if (linger >= 0) {
  35. sock.setSoLinger(true, linger);
  36. }
  37. conn.bind(sock);
  38. // ...
  39. }
  40. }

经过源码解读可以很明确地知道,在HttpClient 4.5.2版本中,设置连接参数有3种方式:

(1)在HttpRequest对象中设置RequestConfig对象属性

(2)在HttpRequest对象中设置HttpParams对象属性.

(3)在连接池对象中设置SocketConfig对象属性

既然找到了根源,下面分别通过这3种方式设置超时参数进行验证.

方式1: 通过RequestConfig对象设置超时参数

  1. int timeOut = 5000;
  2. RequestConfig requestConfig = RequestConfig.custom()
  3. .setConnectionRequestTimeout(timeOut) // 获取连接超时时间
  4. .setConnectTimeout(timeOut) // 设置HTTP连接超时时间
  5. .setSocketTimeout(timeOut) // 设置Socket超时时间
  6. .build();
  7. request.setConfig(requestConfig);

方式2: 通过HttpParams对象设置超时参数

  1. int timeOut = 5000;
  2. HttpParams params = new BasicHttpParams();
  3. params.setParameter(CoreConnectionPNames.SO_TIMEOUT, timeOut); // 设置Socket超时时间
  4. params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, timeOut); // 设置HTTP连接超时时间
  5. request.setParams(params);

方式3: 通过连接池对象设置超时参数

  1. int timeOut = 5000;
  2. PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
  3. // 对连接池设置SocketConfig对象
  4. connManager.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(timeOut).build());
  5. client = HttpClients.custom().setConnectionManager(connManager).build();

通过上述3种方式分别设置超时参数验证,虽然在运行过程中会有报错,但是不会导致线程被阻塞,进程能正常运行结束:

解决问题之后客户端CPU使用率恢复正常:

总结/教训/反思

  1. 之所以会遇到这样的问题,还是因为对HttpClient组件不熟悉导致的;另外,在发现问题之后如何快速定位问题,并搜索稳定的解决方案很重要。

  2. HttpClient组件每个版本的API变化都比较大,在使用时一定要彻底清楚当前使用的版本是如何设置超时时间的。而如何确定知道超时时间控制,通过源代码查看最为妥当.

  3. 在Java平台使用Http客户端组件,可以有多个选择:

    (1)直接使用JDK提供的URL类访问

    (2)使用HttpClient组件,有坑,不同版本设置参数的方式变动较大,最好是阅读一下当前使用版本的源码实现,正确设置好超时时间等参数

    (3)如果使用了Spring MVC框架,还可以使用Spring MVC提供的RestTemplate组件,底层是使用Netty实现的客户端

  4. 遇到的这个坑本身并不属于技术难点,但是面对这个问题的解决思路值得总结:

    (1)程序日志,运行日志非常关键,是定位问题时第一时间需要查看的

    (2)代码review,逐行逐行地审查,首先排除可能存在的代码逻辑问题,比如:死锁等

    (3)通过jstack命令查看线程堆栈信息: jstack -l -F <pid> > stack.log

    (4)通过jmap命令查看内存堆栈信息: jmap -dump:live format=b,file=heap.bin <pid>

    (5)如果结合搜索引擎和上述排查步骤依然未能解决问题,应该第一时间考虑直接阅读组件的源代码实现,特别是使用了开源组件时这可能才是真正解决问题的最佳路径

【参考】

https://blog.csdn.net/u011191463/article/details/78664896 HttpClient超时设置详解

https://my.oschina.net/jywm/blog/1834702 解决httpclient超时设置不生效的问题

https://www.jianshu.com/p/4b3e172c4f2d HttpClient 4.5.2-(四)连接超时的配置

https://www.jianshu.com/p/6a41c95855e3 HttpClient 4.5.2-(五)连接池的配置

https://www.jianshu.com/p/c852cbcf3d68 HttpClient高并发下性能优化-http连接池

https://blog.csdn.net/u011402596/article/details/44619443 HttpClient 多线程处理

https://field-notes.iteye.com/blog/2383759 多线程消费使用HttpClient引发的坑

http://blog.51cto.com/lihao001/1788490 httpclient4.3 导致线程阻塞

https://study121007.iteye.com/blog/2304274 HttpClient4.5.2 连接管理

https://www.jianshu.com/p/c852cbcf3d68 HttpClient高并发下性能优化-http连接池

https://alafqq.iteye.com/blog/2325041 httpclient 多线程执行(网上版本太多了。。。误人子弟)

https://gaozzsoft.iteye.com/blog/2352241 HttpClient 4.5.2版本设置连接超时时间-CloseableHttpClient设置Timeout

https://www.cnblogs.com/softidea/p/6964347.html HttpClient 专题

https://blog.csdn.net/Fhaohaizi/article/details/78217903 httpclient4.5如何确保资源释放

https://blog.csdn.net/u010634066/article/details/83120122 一场HttpClient调用未关闭流引发的问题

https://www.cnblogs.com/mumuxinfei/p/5066633.html Apache HttpClient使用之阻塞陷阱

https://issues.apache.org/jira/browse/HTTPCLIENT-1584 CloseableHttpClient - SSL Handshake has no Socket Timeout

https://monkeyissexy.github.io/2016/11/11/httpclient_ssl_handshake_socketTimeout_bug/ httpclient ssl handshake socketTimeout bug 分析解决过程

http://geekerwang.com/2017/10/22/记录一次坑:socketRead-hang/ 记录一次坑:socketRead hang

https://www.cnblogs.com/jessezeng/p/7448636.html 解决: httpclient ssl 验证导致死锁问题

http://itbang.me/solu/detail/201 JAVA线程卡死问题如何定位?

HttpClient在多线程环境下踩坑总结的更多相关文章

  1. C++多线程环境下的构造函数

    多线程的环境里,我们总不可避免要使用锁.于是一个常见的场景就是: class ObjectWithLock { private: std::mutex mtx_; SomeResType shared ...

  2. React Native 环境搭建踩坑

    React Native (web Android)环境搭建踩坑(真的是一个艰辛的过程,大概所有坑都被我踩了 官方文档地址 : https://facebook.github.io/react-nat ...

  3. C#多线程环境下调用 HttpWebRequest 并发连接限制

    C#多线程环境下调用 HttpWebRequest 并发连接限制 .net 的 HttpWebRequest 或者 WebClient 在多线程情况下存在并发连接限制,这个限制在桌面操作系统如 win ...

  4. Asp.Net在多线程环境下的状态存储问题

    在应用开发中,我们经常需要设置一些上下文(Context)信息,这些上下文信息一般基于当前的会话(Session),比如当前登录用户的个人信息:或者基于当前方法调用栈,比如在同一个调用中涉及的多个层次 ...

  5. SQLite在多线程环境下的应用

    文一 SQLite的FAQ里面已经专门说明,先贴出来.供以后像我目前的入门者学习. (7) 多个应用程序或者同一个应用程序的多个例程能同时存取同一个数据库文件吗? 多进程可以同时打开同一个数据库,也可 ...

  6. 多线程环境下非安全Dictionary引起的“已添加了具有相同键的项”问题

    问题: 代码是在多线程环境下,做了简单的Key是否存的判断, 测试代码如下: public class Program { static Dictionary<string, Logger> ...

  7. 多线程环境下的UI异步操作

    转自原文 多线程环境下的UI异步操作 解决VS中,线程间不可互操作的问题,一揽子解决方案: 一.首先,定义一个类:SetControlProperty using System.Reflection; ...

  8. 单例模式在多线程环境下的lazy模式为什么要加两个if(instance==null)

    刚才在看阿寻的博客”C#设计模式学习笔记-单例模式“时,发现了评论里有几个人在问单例模式在多线程环境下为什么lazy模式要加两个if进行判断,评论中的一个哥们剑过不留痕,给他们写了一个demo来告诉他 ...

  9. 一个由单例模式在多线程环境下引发的 bug

    问题症状 HTTP 日志系统,老是出现日志信息覆盖的情况.比如同时调用 A 接口和 B 接口,B 接口请求响应信息变成了 A 接口请求响应相关信息.这个问题在并发量大的情况下越来越严重. 问题初步分析 ...

随机推荐

  1. DataPipeline创始人&CEO 陈诚:沃森与AI

    引言:本文来自infoQ架构师电子月刊对DataPipeline创始人&CEO陈诚的约稿.陈诚,毕业于上海交大,留学于美国密西根大学,前Yelp大数据研发工程师,曾就职于美国Google.Ye ...

  2. linux系统ubuntu18.04安装mysql(5.7)

    本文是参考mysql官网整理而成,顺便把一些遇到的问题记载下来. ①将MySQLAPT存储库添加到系统的软件存储库列表中  ---->下载APT存储库(下载链接)  ---->安装APT存 ...

  3. background问题

    1.如果是小图的背景图 background: url("@{images-dir}/homepage/our_pro_2x.png") no-repeat 0 0; backgr ...

  4. 随心测试_数据库_003 <数据库存储结构>

    接上篇:了解了_数据库系统组成,继续理解必备知识点:数据库存储_逻辑结构 快速理解 数据存储结构:数据库系统_数据库_表 1. 理解什么是数据库 数据库发展:大致由 人工管理.文件系统.数据库系统(高 ...

  5. linux-高并发与负载均衡-lvs-DR模型试验

    先配置3台虚拟机的网络 3台虚拟机克隆的方法:(....) etho,配置在同一个网段 DIP,RIP在一个网段 node01:作为lvs负载均衡服务器 node02:作为 Real Server n ...

  6. Appuim的安装步骤

    1.下载Appium Desktop并安装 下载地址:https://github.com/appium/appium-desktop/releases 我下载的版本为:appium-desktop- ...

  7. 定时任务调度工作(学习记录 四)schedule与scheduleAtFixedRate的区别

    根据两种情况来看区别 一.首次计划执行的时间早于当前的时间 1.schedule方法 “fixed-delay”:如果第一次执行时间被延迟了,随后的执行时间按照上一次实际执行完成的时间点进行计算 演示 ...

  8. sqlserver2008R2 评估期已过

    早上打开win程序-卡死不动了,查看三层数据库连接-连接不上数据库 打开数据库-提示 评估期已过 解决方法: 进入sqlserver的安装中心-点击 维护-版本升级 输入密钥:企业版:R88PF-GM ...

  9. Power BI For Competition

    It's traditional report design, I'm insufficient for designing that if had a designer to help me wil ...

  10. tensorflow-TFRecord报错ValueError: Protocol message Feature has no "feature" field.

    编写代码用TFRecord数据结构存储数据集信息是报错:ValueError: Protocol message Feature has no "feature" field.或和 ...