前言

今天上午,一个客户反馈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
  • 2018-10-19更新:referencesource的github地址: https://github.com/Microsoft/referencesource

谢谢观看,此篇完毕。

.Net版本依赖之坑引发的搜查的更多相关文章

  1. Spring IO Platform 解决Spring项目组合中版本依赖

    简介: Spring IO Platform是Spring官网中排第一位的项目.它将Spring的核心API集成到一个适用于现代应用程序的平台中.提供了Spring项目组合中的版本依赖.这些依赖关系是 ...

  2. Springcloud的版本依赖问题(最全,包含springCloud所有的版本)

    版权声明:本文为博主原创文章,遵循CC 4.0 BY版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_42105629/article/detai ...

  3. linux版本依赖

    记住, 你的模块代码一定要为每个它要连接的内核版本重新编译 -- 至少, 在缺乏 modversions 时, 这里不涉及因为它们更多的是给内核发布制作者, 而不是开发者. 模块 是紧密结合到一个特殊 ...

  4. Spring Cloud和eureka启动报错 解决版本依赖关系

    导读 An attempt was made to call a method that does not exist. The attempt was made from the following ...

  5. Maven传递依赖的坑:父pom中dependencyManagement版本优先级高于传递依赖版本

    一.由来 之前同事问了个问题,就是当前工程为spring boot项目,假设版本号为2.0.3 这个项目中依赖了一个spring boot项目依赖(先别管为啥有这么奇葩的依赖,这个版本是1.5.9). ...

  6. Spring Cloud 升级最新 Finchley 版本,踩坑指南!

    https://blog.csdn.net/youanyyou/article/details/81530240 Spring Cloud 升级最新 Finchley 版本,踩了所有的坑! 2018年 ...

  7. 安装jumpserver 2.1.2版本遇到的坑

    官方文档地址:https://docs.jumpserver.org/zh/master/install/step_by_step/ Jumpserver 对外需要开放 80 和 2222 端口,如果 ...

  8. Maven的包依赖冲突可引发java.lang.IncompatibleClassChangeError错误

    新版API上线后,发现LOG文件没有正常输出.查看Tomcat的Log文件发现如下的错误信息 May , :: AM com.sun.xml.ws.server.sei.EndpointMethodH ...

  9. IOS低版本遇到了坑不知道你遇到了没

    拿着项目给客户测试,客户那边三个人俩人水果手机是ios8以下版本,结果导致```(恭喜,坑出现!)总不能说老总!"您把版本升级到ios9 吧!

随机推荐

  1. android recovery 升级之USB设备挂载

    Recovery升级过程,通常会从两个地方获取升级包update.zip升级,一般在线升级,会把升级包下载到cache分区,本地升级会从usb或者tf卡中升级.本文讨论下,本地USB升级时,无法挂载U ...

  2. python网络编程:socket、服务端、客户端

    本文内容: socket介绍 TCP: 服务端 客户端 UDP: 服务端 客户端 首发时间:2018-02-08 01:14 修改: 2018-03-20 :重置了布局,增加了UDP 什么是socke ...

  3. nginx 配置简单网站项目(linux下)

    1.新建html2与html3两个网站项目测试,而html是本身就有,记得到/etc/hosts 添加dns记录 2.修改nginx.conf文件 3.测试访问 中间用到一些nginx的命令,就不截图 ...

  4. docker常用命令整理-在容器中使用service命令

    在docker中使用centos镜像启动了容器并安装了相关软件,之后想用service命令启动相关服务却收到如下错误: Failed to get D-Bus connection: Operatio ...

  5. django静态文件

    django静态文件(js脚本.CSS.图片等) 默认统一放在每一个app的static文件夹下, 通过收集静态文件命令,自动将每一个app下static文件夹下的文件复制到根目录的static文件夹 ...

  6. C#语言————第一章 第一个C#程序

    第一章    第一个C#程序 ******************C#程序***************     ①:建立项目:文件-->新建-->项目-->c#-->控制台程 ...

  7. React脚手架创建一个React应用以及项目目录结构详解

    react脚手架 用来帮助程序员快速创建一个基于xxx库的模板项目,包含了所有需要的配置,指定好了所有的依赖,可以直接安装/编译/运行一个简单效果 react提供了一个专门用于创建react项目的脚手 ...

  8. Android 的窗口管理系统 (View, Canvas, WindowManager)

    http://blog.csdn.net/ritterliu/article/details/39295271 From漫天尘沙 在图解Android - Zygote 和 System Server ...

  9. Android清理设备内存具体完整演示样例(二)

    版权声明: https://blog.csdn.net/lfdfhl/article/details/27672913 MainActivity例如以下: package cc.c; import j ...

  10. Lock和Condition在JDK中LinkedBlockingQueue的应用

    Lock和Condition在JDK中LinkedBlockingQueue的应用,核心源码注释解析如下: import java.util.concurrent.LinkedBlockingQueu ...