.Net
.Net
.NET Core 的重要问题
.NET Framework 缺点:
1、系统级别的安装,互相影响
2、无法独立部署
3、ASP.NET 和 IIS 深度耦合
4、ASP.NET 资源消耗大
5、非云原生
.NET Core 的优点:
1、支持独立部署,不互相影响;
2、彻底模块化;
3、没有历史包袱,运行效率高
4、不依赖于 IIS
5、跨平台
6、符合现代开发理念:依赖注入、单元测试等
.NET Core 和.NET Framework 不同:
1、.NET Core 不支持:ASP.NET WebForms、WCF 服务器端(Windows Communication Foundation)、WF、.NET Remoting、Appdomain
2、部分 Windows-Only 的特性.NET core,但是无法跨平台: WinForm、WPF、注册表、Event Log、AD 等。
.NET Standard

.NET Standard 是各个实现通用的基础库规范,规定了一系列需要被所有.NET Core、。NET Framework 及 Xamarin 等共同实现的 API。
验证:.NET Standard 只是标准,不是具体实现
1、建. NET Standard 类库项目,确认版本是 2.0,建一个类,方法中打印 typeof(FileStream).Assembly.Location。
2、分别建. NET Framework 和.NET Core 的控制台项目,添加对类库项目引用,并且调用。
3、用反编译工具 ILSpy(尽管开源)分别反编译 VS 中 FileStream、.NET Framework 和.NET Core 运行中的。BeginRead 方法实现以及定义有不同。
testlib 为.Net Standard 类库:

NetStandard 只包含元数据,不存在可执行代码:
.NET Standard 只是规范,一个.NET Standard 类库可以被支持其版本的.NET Framework、.NET Core、Xamarin 等引用。而.NET Core 类库、.NET Framework 类库则不可以。如果编写一个公用的类库,尽量选择.NET Standard,并且尽量用低版本。
项目结构及发布

程序的发布:
1、部署模式:依赖框架;独立(推荐);目标服务器不需要安装.netcore 运行时
2、目标运行时可移植,任何操作系统都可以
3、生成单个文件。
4、ReadyToRun:AOT(ahead-of-time)、JIT。缺点看文档。
5、裁剪未使用的程序集。缺点看文档。
网站发布
1、不能直接把 bin/Debug 部署到生产环境的服务器上,性能低。应该创建网站的发布版,用【发布】功能。
2、两种部署模式:“框架依赖”和“独立”。两者的优缺点。建议独立模式
3、独立模式为什么要选目标操作系统和 CPU 类型。关于龙芯。
网站的运行
1、在 Windows(SandBox)和 Linux(VMWare Player)里分别运行网站。
2、如何在生产服务器中部署.NET Core 网站。尽管 Kestrel 已经强大到足以作为一个独立的 Web 服务器被使用了,但是一般仍然不会让 Kestrel 直接面对终端用户的请求。配置域名证书、记录请求日志、Url 重写等由反向代理服务器负责。
3、多种部署模式:K8S+容器(推荐,难度高);Linux+Nginx;云平台; Windows+IIS;
网站安全提醒
1、启用 https。
2、如果运维人员需要通过远程桌面或者 SSH 连接到服务器,那么一定要在服务器的防火墙上设置只允许运维人员的 IP 段访问相关端口。
3、严格区分开发环境和生产环境。
4、不要相信客户端请求。
.NET 中使用 FixedTimeEquals 应对计时攻击
计时攻击
在计算机安全中,计时攻击(Timing attack)是旁道攻击 (Side-channel attack) 的一种,而旁道攻击是根据计算机处理过程发出的信息进行分析,包括耗时,声音,功耗等等,这和一般的暴力破解或者利用加密算法本身的弱点进行攻击是不一样的。
假如您有一个后端 webapi, GetConfig 接口用来获取配置信息,调用时需要在 Header 中传入一个秘钥,然后判断是否正确并进行返回,如下
X-Api-Key: x123
[] |
注意,这里我们为了判断两个字符串相等,通常会使用 == 或者 != , 实际上背后使用了 String 的 Equals() 方法,如下
// Determines whether two Strings match. |
而内部又使用了 SequenceEqual() 方法
[] |
大概的逻辑是,先判断两个字符串长度是否一致,如果不是,直接返回 false,然后循环字符串进行逐位对比,一旦发现不相同,直接返回 false,伪代码如下
public bool Equals(string str1, string str2) |
这里有一个问题是,如果字符串第一位不相同,直接就返回 false,如果最后一位不相同,那就需要遍历到最后,然后返回 false。不一样的字符串,计算的时长可能不一致。
尝试破解
假如用户知道了我们的秘钥的固定长度是 4 位。
GET /GetConfig
X-Api-Key:a000
Cost: 2ns
本次耗时了 2ns, 接下来又输入 b000, c000….
GET /GetConfig
X-Api-Key:b000
Cost: 2ns
GET /GetConfig
X-Api-Key:c000
Cost: 2ns
…
GET /GetConfig
X-Api-Key:x000
Cost: 4ns
直到输入了 x000, 发现其他的耗时都是 2ns, 而这里是 4ns,大概率判定第一位是 x。
注意,这里的测试进行了放大,可能每个 case 分别调用了 100 次,然后统计了 P50(中位数)得出的结果。
然后用同样的方法,测试第二位,第三位….., 最终破解拿到了秘钥。
使用固定时间的算法
虽然看上去有点扯,但确实是真实存在的,包括大名鼎鼎的针对 TLS 的 Lucky 13 攻击,有兴趣的同学可以看一下。在安全性要求比较高的场景中,确实要考虑到计时攻击,当涉及到安全时,还是宁可信其有。
所以我们的算法的执行耗时应该是固定的,不应该在不匹配时,就立即返回,我们尝试改造一下代码:
[] |
把 bool 改成了 int 类型,然后使用了运算符 ^ 和 |,同样的,只有字符串全部匹配时,result 为 0,,才会返回 true, 其中任意一个不匹配,result 就不为 0,会返回 false。
最后,为了防止 JIT 对我们的代码进行其他的优化,可以加一个特性[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)],告诉 JIT 不要管它。
上面我们实现了一个针对字符串比较的固定时间的算法,来应对计时攻击。
实际上, 从 .NET Core 2.1 开始就已经做了内置支持,我们可以直接使用 FixedTimeEquals 方法, 看一下它的实现:
[] |
使用:
var result = CryptographicOperations.FixedTimeEquals( |
总结
在安全性比较高的场景中,应该要考虑到计时攻击,可以使用固定时间的算法来应对。在其他的开发语言中,也都有本文中类似的算法,而在 .NET 中,现在我们可以直接使用 CryptographicOperations.FixedTimeEquals。
Playwright
Playwright 是由微软公司 2020 年初发布的新一代自动化测试工具,相较于目前最常用的 Selenium,它仅用一个 API 即可自动执行 Chromium、Firefox、WebKit 等主流浏览器自动化操作,并同时支持以无头模式、有头模式运行。Playwright 提供的自动化技术是绿色的、功能强大、可靠且快速,支持 Linux、Mac 以及 Windows 操作系统。作为针对 Python 语言纯自动化的工具,在回归测试中可更快的实现自动化。
模仿 Web API 中间件 MiddleWare

中间件是 ASP.NET Core 的核心组件,MVC 框架、响应缓存、身份验证、CORS、Swagger 等都是内置中间件。
1、广义上来讲:Tomcat、WebLogic、Redis、IIS;狭义上来讲,ASP.NET Core 中的中间件指 ASP.NET Core 中的一个组件。
2、中间件由前逻辑、next、后逻辑 3 部分组成,前逻辑为第一段要执行的逻辑代码、next 为指向下一个中间件的调用、后逻辑为从下一个中间件执行返回所执行的逻辑代码。每个 HTTP 请求都要经历一系列中间件的处理,每个中间件对于请求进行特定的处理后,再转到下一个中间件,最终的业务逻辑代码执行完成后,响应的内容也会按照处理的相反顺序进行处理,然后形成 HTTP 响应报文返回给客户端。
3、中间件组成一个管道,整个 ASP.NET Core 的执行过程就是 HTTP 请求和响应按照中间件组装的顺序在中间件之间流转的过程。开发人员可以对组成管道的中间件按照需要进行自由组合。
概念
Map、Use 和 Run。Map 用来定义一个管道可以处理哪些请求,Use 和 Run 用来定义管道,一个管道由若干个 Use 和一个 Run 组成,每个 Use 引入一个中间件,而 Run 是用来执行最终的核心应用逻辑。
简单的自定义中间件
1、如果中间件的代码比较复杂,或者我们需要重复使用一个中间件的话,我们最好把中间件的代码放到一个单独的“中间件类”中。
2、中间件类是一个普通的.NET 类,它不需要继承任何父类或者实现任何接口,但是这个类需要有一个构造方法,构造方法至少要有一个 RequestDelegate 类型的参数,这个参数用来指向下一个中间件。这个类还需要定义一个名字为 Invoke 或 InvokeAsync 的方法,方法至少有一个 HttpContext 类型的参数,方法的返回值必须是 Task 类型。中间件类的构造方法和 Invoke(或 InvokeAsync)方法还可以定义其他参数,其他参数的值会通过依赖注入自动赋值。
目标框架:.net6
框架由 MyStaticFilesMiddleware、MyWebAPIMiddleware、NotFoundMiddleware 这 3 个中间件组成.
programs.cs:
var builder = WebApplication.CreateBuilder(args); |
ActionLocator 扫描所有程序集,规定以 controller 结尾的为 controller,注册 controller 到 DI 中,定位请求的 url 为去掉结尾的 Controller 的方法名为 action;
中间件注册顺序重要
MyStaticFilesMiddleware.cs:
public class MyStaticFilesMiddleware |
MyStaticFilesMiddleware 对网站中的静态文件进行处理的中间件
MyWebAPIMiddleware.cs:
public class MyWebAPIMiddleware |
Web API 的核心类对于 MVC 的控制器类做如下简单的约定:
1)控制器的类名以 Controller 结尾,除去结尾的 Controller 就是控制器的名字;
2)控制器中所有的 public 方法就是控制器方法,方法的名字就是控制器的名字;
3)请求路径固定为”/控制器名字/控制方法名字”;
4)控制器的方法不支持重载,不支持通过[HttpGet]等方式绑定特定的 Http 谓词;
5)控制器的方法可以没有参数,如果有参数就只支持一个参数。控制器方法的参数除非是 HttpContext 类型,否则请求报文体会按照 Json 格式进行反序列化;
6)控制器的方法的返回值会被序列化为 Json 字符串,然后发送到响应报文中,不支持 IActionResult 等类型的返回值;
PathParser 从请求路径中分析出来控制器的名字和操作方法的名字,如果路径分析失败,返回值中的 ok 的值为 false,传递给下一个中间件;如果请求路径为”/Test1/Save”,则 Parse 方法的返回值为(true,”Test1”,”Save”)。
NotFoundMiddleware.cs
public class NotFoundMiddleware |
NotFoundMiddleware 如果一个请求不能被管道中任何一个中间件处理,也就是请求的地址不存在,则 ASP.NET Core 会向客户端写入状态码为 404 的响应。为了能够显示自定义的报错信息开发了这个中间件类。一般把其他中间件放到前面,而把 NotFoundMiddleware 放到管道中的最后。这样,如果任何一个中间件能够处理这个请求,这个请求都会被处理,然后短路请求,而如果没有任何一个中间件能够处理这个请求,这个请求就会最终由 NotFoundMiddleware 处理。
参考链接
https://github.com/yangzhongke
.Net 异步编程
同步点餐和异步点餐:
异步点餐一定会提升单个客户点餐速度吗?
比如服务员相当于主线程,异步点餐不能提升单个客户的点餐速度;异步能提高并发,增加 Web 吞吐量。
服务器能够同时服务的请求数量有限,图片美化服务例子:
void BeautifyPic(File photo, Response response)
{
byte[] bytes = 美化图片(photo);
response.Write(bytes);
}
传统多线程开发太麻烦。C#关键字:async、await。async、await 不等于“多线程”。
async、await
“异步方法”:用 async 关键字修饰的方法
1)异步方法的返回值一般是 Task
2)即使方法没有返回值,也最好把返回值声明为非泛型的 Task。
3)调用泛型方法时,一般在方法前加上 await 关,这样拿到的返回值就是泛型指定的 T 类型;
4)异步方法的“传染性”:一个方法中如果有 await 调用,则这个方法也必须修饰为 async
static async Task Main(string[] args) |
如果同样的功能,既有同步方法,又有异步方法,那么首先使用异步方法。.NET 框架中的方法也都支持异步:Main、WinForm 事件处理函数。
对于不支持的异步方法怎么办?
Wait()(无返回值);Result(有返回值)。Result 等待函数执行结果完成返回结果。
风险:死锁。尽量不用。
异步委托:
ThreadPool.QueueUserWorkItem(async(obj) => { |
async、await 原理
static async Task Main(string[] args) |



用 ILSpy 反编译 dll(.exe 只是 windows 下的启动器)成 C# 4.0 版本,就能看到容易理解的底层 IL 代码。await、async 是“语法糖”,最终编译成“状态机调用”。
总结:async 的方法会被 C#编译器编译成一个类,会主要根据 await 调用进行切分为多个状态,对 async 方法的调用会被拆分为对 MoveNext 的调用。用 await 看似是“等待”,经过编译后其实没有“wait”。
async 背后的线程切换
为什么要把一个 async 方法拆分为多个状态然后分为多次调用?“异步可以避免线程等待耗时操作” 但是 await 还是等待呀?
await 调用的等待期间,.NET 会把当前的线程返回给线程池,等异步方法调用执行完毕后,框架会从线程池再取出来一个线程执行后续的代码。
using System.Text; |
Thread.CurrentThread.ManagedThreadId 获得当前线程 Id。验证:在耗时异步(写入大字符串)操作前后分别打印线程 Id。
用餐馆例子思考.NET 这样做的好处。
细节:如果写入内容少,会发现线程 Id 不变。
优化:到要等待的时候,如果发现已经执行结束了,那就没必要再切换线程了,剩下的代码就继续在之前的线程上继续执行了。
异步方法不等于多线程
static async Task<decimal> CalcAsync(int n) |
分别在调用前后打印线程 Id。

把要执行的代码以委托的形式传递给 Task.Run(),这样就会从线程池中取出一个线程执行我们的委托
await Task.Run(() => {
//耗时操作代码,可以 return 返回值
});
用 Task.Run 改造之前的例子,再看看线程 Id 变化。
结论:异步方法的代码并不会自动在新线程中执行,除非把代码放到新线程中执行。
为什么有的异步方法没标 async
看.NET 的 File.ReadAllTextAsync()代码:
async 方法缺点:
1、异步方法会生成一个类,运行效率没有普通方法高;
2、可能会占用非常多的线程;
只甩手 Task,不“拆完了再装”
反编译上面的代码:只是普通的方法调用。
优点:运行效率更高,不会造成线程浪费。
返回值为 Task 的不一定都要标注 async,标注 async 只是让我们可以更方便的 await 而已。
await 相当于把后面代码封装成委托做先驱任务的延续执行。
如果一个异步方法只是对别的异步方法调用的转发,并没有太多复杂的逻辑(比如等待 A 的结果,再调用 B;把 A 调用的返回值拿到内部做一些处理再返回),那么就可以去掉 async 关键字。

string s1 = await ReadFileAsync(1); |
不要用 sleep()
如果想在异步方法中暂停一段时间,不要用 Thread.Sleep(),因为它会阻塞调用线程,而要用 await Task.Delay()。
举例:下载一个网址,3 秒后下载另一个。
using HttpClient httpClient = new HttpClient(); |
在控制台中没看到区别,但是放到 WinForm 程序中就能看到区别了。
ASP.NET Core 中也看不到区别,但是 Sleep()会降低并发,用点餐举例子。
CancellationToken
有时需要提前终止任务,比如:请求超时、用户取消请求。
很多异步方法都有 CancellationToken 参数,用于获得提前终止执行的信号。
CancellationToken 结构体:
None:空
bool IsCancellationRequested 是否取消
(*)Register(Action callback) 注册取消监听
ThrowIfCancellationRequested() 如果任务被取消,执行到这句话就抛异常。
CancellationTokenSource
CancelAfter()超时后发出取消信号
Cancel() 发出取消信号
CancellationToken Token
为“下载一个网址 N 次”的方法增加取消功能。
分别用 GetStringAsync + IsCancellationRequested、 GetStringAsync + ThrowIfCancellationRequested()、带 CancellationToken 的 GetAsync()分别实现。取消分别用超时、用户敲按键(不能 await)实现。
请求超过 5s 后取消:

ASP.NET Core 开发中,一般不需要自己处理 CancellationToken、CancellationTokenSource 这些,只要做到“能转发 CancellationToken 就转发”即可。ASP.NET Core 会对于用户请求中断进行处理。
WhenAll
Task 类的重要方法:
- Task
WhenAny(IEnumerable tasks)等,任何一个 Task 完成,Task 就完成 - Task<TResult[]> WhenAll
(params Task [] tasks)等,所有 Task 完成,Task 才完成。用于等待多个任务执行结束,但是不在乎它们的执行顺序。 - FromResult() 创建普通数值的 Task 对象。
Task<string> t1 = File.ReadAllTextAsync("d:/1.txt"); |
案例:
计算一个文件夹下,所有文本文件的单词个数汇总。
异步其他问题
接口中的异步方法:
async 是提示编译器为异步方法中的 await 代码进行分段处理的,而一个异步方法是否修饰了 async 对于方法的调用者来讲没区别的,因此对于接口中的方法或者抽象方法不能修饰为 async。
异步与 yield:
yield 是 C#为了简化遍历操作实现的语法糖,如果要让某个类型支持遍历就必须要实现系统接口 IEnumerable,这个接口后续实现比较繁琐要写一大堆代码才能支持真正的遍历功能。yield return 不仅能够简化数据的返回,而且可以让数据处理“流水线化”,提升性能。
1、yield 语句的两种形式:
yield return
yield break;
2、使用一个 yield return 语句返回集合的一个元素,yield break 用于终止
3、包含 yield 语句的方法或属性是迭代器。迭代器必须满足以下要求:
a. 返回类型必须是 IEnumerable、IEnumerable
b. 它不能有任何 ref 或 out 参数
4、yield return 语句不能位于 try-catch 快。yield return 语句可以位于 try-finally 的 try 块。yield break 语句可以位于 try 块或 catch 块,但是不能位于 finally 块。
使用链接:
https://www.cnblogs.com/kingcat/archive/2012/07/11/2585943.html
https://www.jianshu.com/p/476b112fd77a
static IEnumerable<string> Test() |
在旧版 C#中,async 方法中不能用 yield。从 C# 8.0 开始,把返回值声明为 IAsyncEnumerable(不要带 Task),然后遍历的时候用 await foreach()即可。
static async Task Main(string[] args) |
ASP.NET Core 和控制台项目中没有 SynchronizationContext,因此不用管 ConfigureAwait(false)等。
不要同步、异步混用。
参考链接
https://github.com/yangzhongke
为什么要使用异步编程?
1.同步方法
static void Main(string[] args) |
主线程在调用同步方法时,会直接在主线程中执行同步方法,这个时候若 SyncTestMethod 方法后面还有其它方法,都需要等待 SyncTestMethod 执行完成。
2. 异步方法
主线程在调用异步方法时,将会新建一个子线程去执行异步方法,调用过 AsyncTestMethod 方法之后,将会直接执行 AsyncTestMethod 后面的方法,这个时候主线程不会等待异步方法执行完成;因为这个时候主线程无法知晓异步方法会在什么时候执行完成,所以此时也无法在主线程中直接获取异步方法的返回,如果需要在异步方法执行完成之后再在主线程中执行其它方法,则需要使用 Wait()来等待异步子线程执行完成。
3. 等待(await)异步方法
主线程在调用异步方法时,将会新建一个子线程去执行异步方法,并且在调用 AsyncTestMethod 方法之后执行了对 AsyncTestMethod 方法的等待 Wait(),这个时候主线程会等待异步方法执行完成,不会执行后续的方法,在 AsyncTestMethod 执行完成之后,等待结束,此时可以拿到异步方法 AsyncTestMethod 的返回值,然后再继续执行主线程中的方法。
4. 同步线程和异步线程关联执行
可以确定的是,在某些情况下,异步编程能够很大的提高我们程序运行的效率,但是大家都在推崇的多使用异步编程不仅仅是因为软件上面的原因,在硬件上也有着很大的原因。将原来跑在一台办公电脑的程序发布到一台双路 E5 的 DELL 的刀片机上面去,结果发现在 DELL 刀片机上面运行的性能竟然比之前的办公电脑还差。
日常开发使用的 Intel CPU 和服务器使用的 CPU 对比:
开发电脑 CPU: 英特尔 ® 酷睿 ™ i5+8500 处理器
处理器基本频率:3GHz
最大睿频频率:4.1GHz
内核数:六核心
线程数:六线程
服务器 CPU: 英特尔 ® 至强 ® D-2177NT 处理器
处理器基本频率:1.90 GHz
最大睿频频率:3.00 GHz
内核数:14
线程数:28
从上面的对比我们可以发现两者之间的差异很明显, i5 处理器的基本频率和最大睿频都高于服务器使用的 至强 处理器,但是在内核数量和线程数量上面却远远不如 至强 ,如果我们的程序全部使用的同步编程的话,以 WebApi 为例,每一次请求中调用的方法都只是在 CPU 的某一个内核/线程中进行的,换句话说,CPU 单核频率的高低直接影响着同步方法的执行效率,而我们之前的程序几乎都是使用了同步方法,在办公电脑上的 i5 处理器和服务器使用的 至强 处理器的单核频率的差异显然就是之前性能问题的直接原因。并且鉴于服务器 CPU 的特性(单核频率低,内核/线程数多),在程序中多使用异步/多线程的方式对于程序的性能而言是无容置疑的。
注意:
虽然异步编程很多时候能提升程序的效率,但不并意味着需要为了使用异步而将所有的方法改为异步执行,如果同步执行的开销甚至比创建一个异步线程开销还低的时候,就完全没有必要再此处使用异步的方式。至于这其中的权衡利弊,或许需要一定的经验才能拿捏的住。
疑问一:await 会阻塞当前线程,等异步线程干完活了再跑?
不会阻塞。当前线程会一直跑进异步方法里,直到遇到要新开一个线程干活时就返回继续跑。此时,新线程在那里干活,主线程干完活就忙其他事去了。新线程干完活,回来 await 的地方把接下来的活继续干了。
疑问二:async+await 和同步执行有什么好处,都是要一个线程干活,一条龙干到底不好吗,非得自己干到一半交给另一个线程去干,别人干活不用开销吗?
这个问题的关键就是有些 IO 事情真的不需要 CPU 派一个线程去干,去了也干不了。比如读写文件,以往同步方法时,派一个线程去读写点东西的时候,这个线程肯定是等在那的,读写这个事情是硬盘去做的,线程就是个使者,告诉硬盘要做哪些事就行,硬盘干完活就告诉线程,线程拿着结果开心地继续干下面的活。那异步就是线程通知硬盘去读写后自己就回去该干嘛干嘛去,不必等在硬盘家门口等消息。硬盘读写的时候,这个线程是非阻塞的,可以干其他活,提高效率。硬盘读写完,会通知 CPU,叫他派一个线程来拿结果,(如果框架是单线程的,那还是这个线程过来)这个线程拿到结果后会继续执行之前那个线程未干完的活,至于上下文信息,底层会交代好,不在这里讨论。这个 IO 事情有很多,比如访问数据库,请求网络等。
参考链接
https://www.jb51.net/article/158544.htm
https://zhuanlan.zhihu.com/p/343235838
TensorFlow.Net
TensorFlow
TensorFlow 是一个深度学习库,由 Google 开源,可以对定义在 Tensor(张量)上的函数自动求导。Tensor(张量)意味着 N 维数组,Flow(流)意味着基于数据流图的计算,TensorFlow 即为张量从图的一端流动到另一端。它的一大亮点是支持异构设备分布式计算,它能够在各个平台上自动运行模型,从电话、单个 CPU / GPU 到成百上千 GPU 卡组成的分布式系统。支持 CNN、RNN 和 LSTM 算法,是目前在 Image,NLP 最流行的深度神经网络模型。
为什么需要 TensorFlow 等库
深度学习通常意味着建立具有很多层的大规模的神经网络。除了输入 X,函数还使用一系列参数,其中包括标量值、向量以及最昂贵的矩阵和高阶张量。在训练网络之前,需要定义一个代价函数,常见的代价函数包括回归问题的方差以及分类时候的交叉熵。训练时,需要连续的将多批新输入投入网络,对所有的参数求导后,代入代价函数,从而更新整个网络模型。
这个过程中有两个主要的问题:
- 较大的数字或者张量在一起相乘百万次的处理,使得整个模型代价非常大。
- 手动求导耗时非常久。
所以 TensorFlow 的对函数自动求导以及分布式计算,可以帮我们节省很多时间来训练模型。
TensorFlow 的优点
第一,基于 Python,写的很快并且具有可读性。
第二,在多 GPU 系统上的运行更为顺畅。
第三,代码编译效率较高。
第四,社区发展的非常迅速并且活跃。
第五,能够生成显示网络拓扑结构和性能的可视化图。
TensorFlow 的工作原理
TensorFlow 是用数据流图(data flow graphs)技术来进行数值计算的。数据流图是描述有向图中的数值计算过程。有向图中,节点通常代表数学运算,边表示节点之间的某种联系,它负责传输多维数据(Tensors)。节点可以被分配到多个计算设备上,可以异步和并行地执行操作。因为是有向图,所以只有等到之前的入度节点们的计算状态完成后,当前节点才能执行操作








