首页 > 其他分享 >死锁【3】

死锁【3】

时间:2022-11-02 00:24:02浏览次数:29  
标签:string Unknown 死锁 线程 result httpResponseMessage httpClient

api接口无响应,呈现了hangon现象,从一些过往经验看,大概也只有三种情况。

  • 大量锁等待

  • 线程不够用

  • 死锁

有了这种先入为主的思想,那就上windbg说事呗。

windbg 分析

1. 有大量锁等待吗?

要想看是否锁等待,老规矩,看一下 同步块表

0:000> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner
-----------------------------
Total           1673
CCW             3
RCW             4
ComClassFactory 0
Free            397

扑了个空,啥也没有,那就暴力看看所有的线程栈吧。

 

 不看还好,一看吓一跳,有339个线程卡在了 System.Threading.Monitor.ObjWait(Boolean, Int32, System.Object) 处,不过转念一想,就算有339个线程卡在这里,真的会导致程序hangon吗? 也不一定,毕竟我看过有1000+的线程也不会卡死,只不过cpu爆高而已,接下来继续研判一下是不是线程不够用导致,可以从 线程池任务队列 上面入手。

探究线程池队列

可以用 !tp 命令查看。

0:000> !tp
CPU utilization: 10%
Worker Thread: Total: 328 Running: 328 Idle: 0 MaxLimit: 32767 MinLimit: 4
Work Request in Queue: 74
    Unknown Function: 00007ffe91cc17d0  Context: 000001938b5d8d98
    Unknown Function: 00007ffe91cc17d0  Context: 000001938b540238
    Unknown Function: 00007ffe91cc17d0  Context: 000001938b5eec08
    ...
    Unknown Function: 00007ffe91cc17d0  Context: 0000019390552948
    Unknown Function: 00007ffe91cc17d0  Context: 0000019390562398
    Unknown Function: 00007ffe91cc17d0  Context: 0000019390555b30
--------------------------------------
Number of Timers: 0
--------------------------------------
Completion Port Thread:Total: 5 Free: 4 MaxFree: 8 CurrentLimit: 4 MaxLimit: 1000 MinLimit: 4

从输出信息看,线程池中328个线程全部打满,工作队列中还有74位客人在等待,综合这两点信息就已经很清楚了,本次hangon是由于大量的客人到来超出了线程池的接待能力所致。

接待能力真的不行吗?

这个标题我觉得很好,真的不行吗? 到底行不行,可以从两点入手:

  • 是不是代码写的烂?

  • qps是不是真的超出了接待能力?

要想找出答案,还得从那 339 个卡死的线程说起,仔细研究了下每一个线程的调用栈,大概卡死在这三个地方。

1.GetModel

ublic static T Get<T>(string url, string serviceModuleName)
{
    try
    {
        T val3 = default(T);
        HttpClient httpClient = TryGetClient(serviceModuleName, true);
        using (HttpResponseMessage httpResponseMessage = httpClient.GetAsync(GetRelativeRquestUrl(url, serviceModuleName, true)).Result)
        {
            if (httpResponseMessage.IsSuccessStatusCode)
            {
                string result = httpResponseMessage.Content.ReadAsStringAsync().Result;
                if (!string.IsNullOrEmpty(result))
                {
                    val3 = JsonConvert.DeserializeObject<T>(result);
                }
            }
        }
        T val4 = val3;
        val5 = val4;
        return val5;
    }
    catch (Exception exception)
    {
        throw;
    }
}

2.GetStreamByApi

public static Stream GetStreamByApi<T>(string url, T content)
{
    Stream result = null;
    HttpClientHandler httpClientHandler = new HttpClientHandler();
    httpClientHandler.AutomaticDecompression = DecompressionMethods.GZip;
    HttpClientHandler handler = httpClientHandler;
    using (HttpClient httpClient = new HttpClient(handler))
    {
        httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream"));
        string content2 = JsonConvert.SerializeObject((object)content);
        HttpContent httpContent = new StringContent(content2);
        httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
        HttpResponseMessage result2 = httpClient.PostAsync(url, httpContent).Result;
        if (result2.IsSuccessStatusCode)
        {
            result = result2.Content.ReadAsStreamAsync().Result;
        }
        httpContent.Dispose();
        return result;
    }
}

3. Get

public static T Get<T>(string url, string serviceModuleName)
{
    try
    {
        T val3 = default(T);
        HttpClient httpClient = TryGetClient(serviceModuleName, true);
        using (HttpResponseMessage httpResponseMessage = httpClient.GetAsync(GetRelativeRquestUrl(url, serviceModuleName, true)).Result)
        {
            if (httpResponseMessage.IsSuccessStatusCode)
            {
                string result = httpResponseMessage.Content.ReadAsStringAsync().Result;
                if (!string.IsNullOrEmpty(result))
                {
                    val3 = JsonConvert.DeserializeObject<T>(result);
                }
            }
        }
        T val4 = val3;
        val5 = val4;
        return val5;
    }
    catch (Exception exception)
    {
        throw;
    }
}

寻找真相

上面我罗列的这三个方法的代码,不知道大家可看出什么问题了? 对,就是 异步方法同步化,这种写法本身就很低效,主要表现在2个方面。

  • 开闭线程本身就是一个相对耗费资源和低效的操作。

  • 频繁的线程调度给了cpu巨大的压力

而且这种写法在请求量比较小的情况下还看不出什么问题,一旦请求量稍大一些,马上就会遇到该dump的这种情况。

总结

综合来看这次hangon事故是由于开发人员 异步方法不会异步化 导致,改法很简单,进行纯异步化改造 (await,async),解放调用线程,充分利用驱动设备的能力。

这个dump也让我想起了 CLR Via C# 书中(P646,647) 在讲用 await,async 来改造 同步请求 的例子 。

 

 

我觉得这个dump就是该例子的最好佐证

 

标签:string,Unknown,死锁,线程,result,httpResponseMessage,httpClient
From: https://www.cnblogs.com/wwkk/p/16849669.html

相关文章

  • mysql并发插入死锁
    前言开发中遇到多线程并发情况对数据批量插入主键id非雪花id,函数自增id原理分析两个事务都持有该行的S锁,期望获取X锁时被对方阻塞了。通俗讲就是并发插入出现相同......
  • Java:死锁及避免方法
    死锁因为我其他文章有贴过死锁的代码,就不贴了。说下什么是死锁。举个生活的例子:两个女孩打架,互相揪着对方的头发不松手,让对方先放她才放。嗯,这个情况就是死锁。死锁的必......
  • 【Java】线程的死锁
    1.死锁不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。说明:出现死锁后,不会出现异常,不会出现提示,只是所有的线程......
  • java的死锁与解决方法
    一、什么是死锁?死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无限等待。 二、产生死锁的原因与......
  • 【SQL】查死锁
    --查看被锁表:selectrequest_session_idspid,OBJECT_NAME(resource_associated_entity_id)tableNamefromsys.dm_tran_lockswhereresource_type='OBJECT'orderbytabl......
  • 查看oracle死锁
    selectA.sid,b.serial#,decode(A.type,'MR','MediaRecovery','RT','RedoThread','UN','UserName','TX','Transaction','TM','DM......
  • Java死锁演示和原理
    packageA_ShangGuiGu.Thread.ThreadTest;/***演示死锁问题*1.死锁的理解:不同的线程分别占用对方需要的同步资源,都在等待对方释放自己所需的另一个资源,就形成了死锁。......
  • java死锁写法
    死锁产生条件:多线程,多个锁,锁嵌套(拿到资源1后还想要别人已经获取的资源2)packageThread;/**死锁产生条件:多线程,多个锁,锁嵌套*/publicclassDeadLockDemo{publi......
  • Java 多线程(九)死锁及Lock
    死锁多个线程各自站有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有“两个......
  • SQL server 处理死锁
    杀掉死锁的sqlserver进程 SELECTrequest_session_idspid,OBJECT_NAME(resource_associated_entity_id)tableName  FROMsys.dm_tran_locks  WHEREresource_......