.Net版本依赖之坑引发的搜查

前言

今天上午,一个客户反馈XX消息没有推送到第三方链接。于是我查看了推送日志列表,并没有今天的。接着登录服务器查询文件日志,看到了记录。我们的代码步骤是消息先推送到消息队列,消费消息队列时,记录文件日志,然后异步推送到第三方。

调试排坑

经过一番寒彻骨的查询几个关键表,构造数据,并调试推送后,发现了问题源头,是Json版本依赖问题引发的坑,然后修改版本号发布解决了。下面让我们用一个简易的Demo重现下问题所在。

问题重现

构建环境

首先新建基于.NetFrameWork 4.5.1版本的类库ErrorSets.CommonE和控制台程序ConsoleApp1

.Net FrameWork

然后ErrorSets.CommonE引入Newtonsoft.Json v11.0.2
Newtonsoft.Json

然后模拟两个推送接口,一个是用Task包装的,一个是正常方法。

public class SeriEx
    {
        public static void TaskPostThird(object obj)
        {
            Task.Factory.StartNew(() =>
            {
                var data = JsonConvert.SerializeObject(obj);
                Console.WriteLine($"TaskPostThird:{data}");                

            });
        }
        public static void PostThird(object obj)
        {
            var data = JsonConvert.SerializeObject(obj);
            Console.WriteLine($"PostThird:{data}");
        }
    }

ConsoleApp1引入Newtonsoft.Json v9.0.1

image.png

引入调用代码

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var user = new User() { Mobile = "12546423" };
           // SeriEx.PostThird(user);
            SeriEx.TaskPostThird(user);

            Console.WriteLine("End");
            Console.ReadKey();
        }
    }
}

查看运行结果

当Main调用SeriEx.TaskPostThird(user)时,结果令我们失望。既没有报错,也没有执行方法体内输出。

image.png

改动TaskPostThird,加上异常捕获

 public static void TaskPostThird(object obj)
        {
            Task.Factory.StartNew(() =>
            {
                try
                {
                    var data = JsonConvert.SerializeObject(obj);
                    Console.WriteLine($"TaskPostThird:{data}");
                }
                catch (Exception ex) {
                    Console.WriteLine($"TaskPostThirdEx:{ex.Message}");
                }
            });
        }

运行程序后,依然没有异常抛出,也没有任何结果输出。让我们将Main调用改成不带Task的SeriEx.PostThird(user)

class Program
    {
        static void Main(string[] args)
        {
            var user = new User() { Mobile = "12546423" };
            SeriEx.PostThird(user);
            Console.WriteLine("End");
            Console.ReadKey();
        }
    }

执行结果如下,抛出了异常。结果符合预期,只要不是沉默的代码就好!


异常

根据异常提示,我们在app.config追加如下配置oldVersion改成0.0.0.0-11.0.0.0,支持不同版本。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
    </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="9.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

再运行程序,结果如下。达到我们预期


image.png

为了更直观的显示Task内部异常问题,我们用下面代码:

            Task.Factory.StartNew(() =>
            {
                throw new Exception("Exxx");
            });
        }

运行结果毫无反应,可以猜测Task.Factory内部屏蔽了异常?

NetCore下是否同样问题?

按照相同的套路,建一个基于.netCore2.1的类库ErrorSets.CommonCore和控制台程序ConsoleAppCore,并引入不同版本的Newtonsoft.Json。编译结果如下:

编译报错

我们可以看到.NetCore版本直接提示报错,编译失败。让我们来继续测试下另外一个问题Task.Factory.StartNew下异常问题。为了编译通过,先移除json。

 static void Main(string[] args)
        {
            var user = new User() { Mobile = "12546423" };
            TaskThrowExcetption();
            Console.ReadKey();
        }
        static void TaskThrowExcetption() {
            Task.Factory.StartNew(() =>
            {
                throw new Exception("s");
            });
        }

运行结果一片空白,并没有跑错。说明.NetCore下也是屏蔽了异常。

中断的源码路

根据F12提示,看到Task引用的是System.Runtime.dll,所以我下载了corefx

#region 程序集 System.Runtime, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// C:\Users\hp\.nuget\packages\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Runtime.dll
#endregion

打开src\System.Runtime,解决方案搜索Factory,迎接我的是

public partial class TaskFactory
    {
        public TaskFactory() { }
 public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<object, TResult> function, object state) { throw null; }
        public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<object, TResult> function, object state, System.Threading.CancellationToken cancellationToken) { throw null; }
        public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<object, TResult> function, object state, System.Threading.CancellationToken cancellationToken, System.Threading.Tasks.TaskCreationOptions creationOptions, System.Threading.Tasks.TaskScheduler scheduler) { throw null; }
        public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<object, TResult> function, object state, System.Threading.Tasks.TaskCreationOptions creationOptions) { throw null; }
        public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<TResult> function) { throw null; }
        public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<TResult> function, System.Threading.CancellationToken cancellationToken) { throw null; }
        public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<TResult> function, System.Threading.CancellationToken cancellationToken, System.Threading.Tasks.TaskCreationOptions creationOptions, System.Threading.Tasks.TaskScheduler scheduler) { throw null; }
        public System.Threading.Tasks.Task<TResult> StartNew<TResult>(System.Func<TResult> function, System.Threading.Tasks.TaskCreationOptions creationOptions) { throw null; }
    }

令人失望的partial,令人失望的throw null;我打开了另外一个src\System.Threading.Tasks,看到了项目一片空白!!!

Bing下生物

一片迷茫之下,我使用了Bing,国际版搜索“task.factory.startnew sourcecode”,第一条是https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs,结果让我惊喜!
在微软的搜索框内,我输入TaskFactory,出现如下结果:

TaskFactory

        public Task StartNew(Action action)
        {
            StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
            Task currTask = Task.InternalCurrent;
            return Task.InternalStartNew(currTask, action, null, m_defaultCancellationToken, GetDefaultScheduler(currTask),
                m_defaultCreationOptions, InternalTaskOptions.None, ref stackMark);
        }

Task.InternalStartNew如下:

 internal static Task InternalStartNew(
             Task creatingTask, Delegate action, object state, CancellationToken cancellationToken, TaskScheduler scheduler,
             TaskCreationOptions options, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark)
        {
            // Validate arguments.
            if (scheduler == null)
            {
                throw new ArgumentNullException("scheduler");
            }
            Contract.EndContractBlock();

            // Create and schedule the task. This throws an InvalidOperationException if already shut down.
            // Here we add the InternalTaskOptions.QueuedByRuntime to the internalOptions, so that TaskConstructorCore can skip the cancellation token registration
            Task t = new Task(action, state, creatingTask, cancellationToken, options, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler);
            t.PossiblyCaptureContext(ref stackMark);

            t.ScheduleAndStart(false);
            return t;
        }

查到这里,我累了。工作到此结束。。。

微软.net源码链接已收藏到.NetCore外国一些高质量博客分享,保持长期更新

源码

源码查看问题记录集,欢迎 Star

总结

今日发现了三个问题如下:

  • Task.Factory.StartNew方法体内不会抛出异常【原因:主线程默认不捕获异步线程的异常】。
  • 针对json不同版本,.net framework可以编译通过,.netcore编译失败。
  • .net framework可以通过配置文件解决版本问题,那么.netcore是如何解决的?
    今天最重要的是发现了微软.net源码网址,不在github,在他自己的老家https://referencesource.microsoft.com

谢谢观看,此篇完毕。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容