.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
谢谢观看,此篇完毕。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?