.Net版本依赖之坑引发的搜查
前言
今天上午,一个客户反馈XX消息没有推送到第三方链接。于是我查看了推送日志列表,并没有今天的。接着登录服务器查询文件日志,看到了记录。我们的代码步骤是消息先推送到消息队列,消费消息队列时,记录文件日志,然后异步推送到第三方。
调试排坑
经过一番寒彻骨的查询几个关键表,构造数据,并调试推送后,发现了问题源头,是Json版本依赖问题引发的坑,然后修改版本号发布解决了。下面让我们用一个简易的Demo重现下问题所在。
问题重现
构建环境
首先新建基于.NetFrameWork 4.5.1版本的类库ErrorSets.CommonE和控制台程序ConsoleApp1

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

然后模拟两个推送接口,一个是用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

引入调用代码
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)时,结果令我们失望。既没有报错,也没有执行方法体内输出。

改动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>
再运行程序,结果如下。达到我们预期

为了更直观的显示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,出现如下结果:

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版本依赖之坑引发的搜查的更多相关文章
- Spring IO Platform 解决Spring项目组合中版本依赖
简介: Spring IO Platform是Spring官网中排第一位的项目.它将Spring的核心API集成到一个适用于现代应用程序的平台中.提供了Spring项目组合中的版本依赖.这些依赖关系是 ...
- Springcloud的版本依赖问题(最全,包含springCloud所有的版本)
版权声明:本文为博主原创文章,遵循CC 4.0 BY版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_42105629/article/detai ...
- linux版本依赖
记住, 你的模块代码一定要为每个它要连接的内核版本重新编译 -- 至少, 在缺乏 modversions 时, 这里不涉及因为它们更多的是给内核发布制作者, 而不是开发者. 模块 是紧密结合到一个特殊 ...
- Spring Cloud和eureka启动报错 解决版本依赖关系
导读 An attempt was made to call a method that does not exist. The attempt was made from the following ...
- Maven传递依赖的坑:父pom中dependencyManagement版本优先级高于传递依赖版本
一.由来 之前同事问了个问题,就是当前工程为spring boot项目,假设版本号为2.0.3 这个项目中依赖了一个spring boot项目依赖(先别管为啥有这么奇葩的依赖,这个版本是1.5.9). ...
- Spring Cloud 升级最新 Finchley 版本,踩坑指南!
https://blog.csdn.net/youanyyou/article/details/81530240 Spring Cloud 升级最新 Finchley 版本,踩了所有的坑! 2018年 ...
- 安装jumpserver 2.1.2版本遇到的坑
官方文档地址:https://docs.jumpserver.org/zh/master/install/step_by_step/ Jumpserver 对外需要开放 80 和 2222 端口,如果 ...
- Maven的包依赖冲突可引发java.lang.IncompatibleClassChangeError错误
新版API上线后,发现LOG文件没有正常输出.查看Tomcat的Log文件发现如下的错误信息 May , :: AM com.sun.xml.ws.server.sei.EndpointMethodH ...
- IOS低版本遇到了坑不知道你遇到了没
拿着项目给客户测试,客户那边三个人俩人水果手机是ios8以下版本,结果导致```(恭喜,坑出现!)总不能说老总!"您把版本升级到ios9 吧!
随机推荐
- 从零自学Java-9.描述对象
1.为类或对象创建变量:2.使用对象和类的方法:3.调用方法并返回一个值:4.创建构造函数:5.给方法传递参数:6.使用this来引用对象:7.创建新对象. 程序VirusLab:测试Virus类的功 ...
- 在 Azure Resource Manager 模板中使用托管磁盘
本文介绍使用 Azure Resource Manager 模板预配虚拟机时托管与非托管磁盘之间的差异. 这有助于将现有模板从使用非托管磁盘更新为使用托管磁盘. 我们将使用 101-vm-simple ...
- 如何创建和还原SQL Server 2000数据库?
说明:这篇文章是几年前我发布在网易博客当中的原创文章,但由于网易博客现在要停止运营了,所以我就把这篇文章搬了过来,虽然现如今SQL Server 2000软件早已经过时了,但仍然有一部分人在使用它,尤 ...
- linux服务器系统盘坏且系统盘为软raid的修复方法
1 需要换新盘的情况 1.1 一块盘grub损坏修复 一块盘grub损坏修复(可通过另一块盘进入系统的情况).更换硬盘的方式,可以热插拔,也可以服务器断电后更换,但如果是热插拔,可能会导致盘符变更.坏 ...
- Oracle 11g 发行版2的安装,PLSQL_Developer安装 , Oracle数据库安装失败,完全卸载,常用的命令
Oracle 11g 发行版2的安装 PLSQL_Developer安装 Oracle数据库安装失败,完全卸载oracle11g 常用的命令 Oracle 11g 发行版2的安装 1. 下载 下载地址 ...
- 进程间通信——LINUX
1.编写一段程序,使用系统调用fork( )创建两个子进程,再用系统调用signal( )让父进 程捕捉键盘上来的中断信号(即按ctrl+c键),当捕捉到中断信号后,父进程用系统调用kill( )向 ...
- myeclipce项目导入eclipse中报错
1 找到新建页面所在的工程名字,然后左键选中,右键弹出功能菜单,选择Build Path,进入配置路径. 2 在java build path 页面的下选择Libraries栏目(默认选择),点击右侧 ...
- January 16th, 2018 Week 03rd Tuesday
Accept who you are, and revel in it. 接受真实的自己并乐在其中. Try to accept youself and try to love yourself mo ...
- 使用golang求出A-Z的所有子集
参考链接:https://blog.csdn.net/K346K346/article/details/80436430 有一个集合由A-Z这26个字母组成,打印这个集合的所有子集,每个子集一行,写C ...
- [CQOI2016]K远点对
嘟嘟嘟 做过[国家集训队]JZPFAR这道题的话,这题就不难了. 我们维护一个长度为\(k\)的小根堆,在加入第\(i\)个点之前,用\([1, i - 1]\)这些点离点\(i\)的距离更新答案.这 ...