一次Task.Run异常问题的排查

        最近在测试一个功能代码时发现一个非常奇怪的问题,主要是Task.Run引起一些不符合逻辑的错误,以下针对这一问题排查的总结。

问题代码

        可以建个控制台程序来运行以下代码

    class Program
    {
        static User user = new User();
        static void Main(string[] args)
        {
            for (int i = 0; i < 50; i++)
            {
                Task.Run(user.Init);
            }
            System.Threading.Thread.Sleep(-1);
        }
    }


    public class User
    {


        private bool mInit = false;


        private Task OnInit()
        {
            Console.WriteLine("User init");
            System.Threading.Thread.Sleep(1000);
            return Task.CompletedTask;
        }


        public void Init()
        {
            lock (typeof(User))
            {
                if (!mInit)
                {


                    var task = Task.Run(this.OnInit);
                    if (!task.Wait(5000))
                    {
                        throw new TimeoutException("user init error!");
                    }
                    mInit = true;
                }
            }


        }
    }

以上代码执行的结果非常奇怪,当在Debug模式下运行,会抛出超时错误。

运行在release模式下则会引起OnIint方法被执行多次,lock完全起不了作用。。

和朋友讨论过程中说lock不要和Task.Run混用,但Task.Wait的实现是基于线程信号量的和async/await是有着本质的差异。抱着解决问题的思路把Task.Run直接改成了线程池方式运行,但结果还是一样。由于找不到问题原因最终去dotnet上提个issues,看一下能提供什么意见。

问题的发现

        对于一个程序员来说问题没解决怎能安心呢,隔一天issues没有响应于是开启的解决问题的碰撞模式。在throw timeout里打个断点看一下情况,结果无意中发现Task的状态是WaitingForActivation

状态描述是等待内部调度激活,意思是说这代码并不是不执行或执行有问题,而因为某些状态导致Task还在等待执行中。然后针对这一问题在网查找了一下才发现这问题的原因,主要问题是for 50已经把线和池中的线程抽光了,然然后在Init方法使用Task.Run的时候就只能等待。。。加上方法后面Task.Wait导致当前线程无法回归到池,所以就只能引起超时间异常!如果这里的Task.Wait不加上个超时,那这测试代码就直接处于假死状态无法继续工作,一个等待一个试图获取线程操作从而形成一个类似于死锁的问题!

总结

        当你在使用Task.Run时出现一些非常意想不到的结果时可以通过Task.Status状态可以更好的定位到问题。Task默认也是基于线程池的,所以在使用Task.Run和Task.Wait的就要注意这一点,虽然可以通过加大线程池的最小数量来解决低并发问题,但高并发下还是会存在线程资源不足的情况;为了确保不出现类似于死锁的问题,请在使用Task.Wait必须加上超时时间,并且是越短越好,毕竟Wait方法是基于线程阻塞。

BeetleX

开源跨平台通讯框架(支持TLS)
轻松实现高性能:tcp、http、websocket、redis、rpc和网关等服务应用

https://beetlex.io

如果你想了解某方面的知识或文章可以把想法发送到

[email protected]|[email protected]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章