深入理解 ValueTask

.NET Framework 4 里面的命名空间为 System.Threading.Tasks的 Task 类。这个类以及它派生的 Task<TResult> 早已成为编程的主要部分,在 C#5 中的异步编程模式当作介绍了 async/await。在这篇文章里,我会覆盖新的类 ValueTask / ValueTask<TResult>,介绍它们在通用的使用上降低内存消耗来提高异步性能,这是非常重要的。

Task

Task 有多种用途,但是它的核心就是 “promise”,它表示最终完成的一些操作。你初始化一个操作,并返回给它一个 Task,它当操作完成的时候它会完成,这可能作为初始化操作的一部分同步发生(比如访问一个早就缓冲好了的缓冲区),也有能是异步的,在你完成这个任务时(比如访问一些还没有缓冲好的字节,但是很快就缓冲好了可以访问),或者是在你已经接收 Task 的时候异步完成(比如通过网络访问数据)。因为操作完成可能是异步的,所以你需要为结果等待它(但这经常违背异步编程的初衷)或者你必须提供一个回调函数来调用,当这个操作完成的时候。在 .NET 4 中,提供了如回调函数一样的来实现如 Task.ContinueWith 方法,它暴露通过传递一个委托的回调函数,这个函数在 Task 完成时触发:

SomeOperationAsync().ContinueWith(task =>{try {TResult result = task.Result;UseResult(result);} catch (Exception e) {HandleException(e);}
})

但是在 C# 5 以及 .NET Framwrok 4.5 中,Task 只需要 await 这样就能很简单的获取这个异步操作完成返回的结果,它生成的代码能够优化上述所有情况,无论操作是否同步完成,是否异步完成,还是在已经提供的回调异步完成,都可以正确地处理事情。

TResult result = await SomeOperationAsync();
UseResult(result);

Task 很灵活,并且有很多好处。例如你可以通过多个消费者并行等待多次。你可以存储一个到字典中,以便后面的任意数量的消费者等待,它允许为异步结果像缓存一样使用。如果场景需要,你可以阻塞一个等待完成。并且你可以在这些任务上编写和使用各种操作(就像组合器),例如 WhenAny 操作,它可以异步等待第一个操作完成。

然而,这种灵活性对于大多数情况下是不需要的:仅仅只是调用异步操作并且等待结果:

TResult result = await SomeOperationAsync();
UseResult(result);

如上用法,我们根本不需要多次等待,我们不需要处理并行等待,我们也不需要处理异步阻塞,我们更不需要去写组合器。我们只是简单的等待异步操作 promise 返回的结果。这就是全部,我们怎么写异步代码(例如 TResult = SomeOperation();),也能很自然而然的用 async / await

进一步说,Task 会有潜在的副作用,在特定的场景中,这个例子被大量创建,并且高吞吐和高性能为关键概念:Task 是一个类。作为一个类,就是说任意操作创建一个 Task 都会分配一个对象,越来越多的对象都会被分配,所以 GC 操作也会越来越频繁,也就会消耗越来越多的资源,本来它应该是去做其他事的。

运行时和核心库能减轻大多数这种场景。举个例子,如果你写了如下代码:

public async Task WriteAsync(byte value)
{if(_bufferedCount == _buffer.Length){await FlushAsync();}_buffer[_bufferedCount++] = value;
}

在常规的例子中,缓冲区有可用空间,并且操作是同步完成。当这样运行的时候,这里返回的 Task 没有任何特别之处,因为它不会返回值:这个返回 Task 就等价于返回一个 void 的同步方法。尽管如此,运行时能够简单缓存单个非泛型的 Task 以及对于所有的 async Task 同步完成的方法都能重复使用它(暴露的缓存的单例就像是 Task.CompletedTask)。例如你的代码可以这样:

public async Task<bool> MoveNextAsync()
{if(_bufferedCount == 0){await FillBuffer();}return _bufferedCount > 0;
}

通常情况下,我们期望会有一些数据被缓冲,在这个例子中,这个方法检查 _bufferedCount,检验值大于 0,并返回 true;只有当前缓冲区域没有缓冲数据时,它才需要执行可能是异步完成的操作。由于这里是 Boolean 存在两个可能的结果(true 和 false),这里可能只有两个对象 Task<bool>,它需要表示所有可能的结果值,所以运行时会缓存两个对象,以及简单返回一个 Task<bool> 的缓存对象,它的结果值为 true 来避免必要的分配。只有当操作异步完成时,这个方法需要分配一个新的 Task<bool>,因为在它知道这个操作的结果之前,它需要将对象返回给调用者,并且还要必须有一个唯一的对象,当操作完成的时候将它存进去。

运行时为其他的类型很好的维护一个很小的缓存,但是用它来存储所有是行不通的。例如下面方法:

public async Task<int> ReadNextByteAsync()
{if(_bufferedCount == 0){await FillBuffer();}if(_bufferedCount == 0) {return -1;}_bufferedCount--;return _buffer[_pisition++];
}

也经常会同步完成。但是不像 Boolean 那个例子,这个方法返回一个 Int32 的值,它大约有 40 亿中可能的值,并且为所有的情况缓存一个 Task<int>,将会消耗可能数以百计的千兆字节内存。运行时为 Task<int> 负责维护一个小的缓存,但是只有很小部分结果的值有用到,例如如果是同步完成的(数据缓存到缓存区),返回值如 4,它最后使用了缓存 task,但是如果这个操作是同步完成返回结果值如 42,它最后将分配一个新的 Task<int>,就类似调用 Task.FromResult(42)

很多库实现了尝试通过维护它们自己的缓存来降低这个特性。例如 .NET Framwork 4.5 的 MemoryStream.ReadAsync 重载函数总是同步完成的,因为它只是从内存中读数据。ReadAsync 返回一个 Task<int>,这个 Int32 结果值表示读的字节数。ReadAsync 经常用在循环中,表示每次调用请求的字节数,ReadAsync 能够完全满足这个请求。因此,通常情况下的请求重复调用 ReadAsync 来同步返回一个 Task<int>,其结果与之前的调用相同。因此,MemoryStream 维护单个 task 的缓存,它成功返回最后一个 task。然后再接着调用,如果这个新的结果与缓存的 Task<int> 匹配,它只返回已经缓存的。否则,它会使用 Task.FromResult 来创建一个新的,存储到新的缓存 task 并返回。

即使如此,还有很多案例,这些操作同步完成并且强制分配一个 Task<TResult> 返回。

ValueTask<TResult> 和同步完成

所有的这些都引发 .NET CORE 2.0 引入了一个新类型,可用于之前的 .NET 版本 ,在System.Threading.Tasks.Extensions Nuget 包中:ValueTask<TResult>

ValueTask<TResult> 在 .NET Core 2.0 作为结构体引入的,它是 TResult 或 Task<TResult> 包装器。也就是说它能从异步方法返回并且如果这个方法同步成功完成,不需要分配任何内存:我们只是简单的初始化这个 ValueTask<TResult> 结构体,它返回 TResult。只有当这个方法异步完成时,Task<Result> 才需要被分配,通过 被创建的ValueTask<TResult> 来包装这个实力对象(为了最小化的大小的 ValueTask<TResult> 以及优化成功路径,一个异步方法它出现故障,并且出现未处理的异常,它还是会分配一个 Task<TResult> 对象,所以 ValueTask<TResult>能简单的包装 Task<TResult> 而不是必须添加额外的字段来存储异常信息)。

于是,像 MemoryStream.ReadAsync 这个方法,它返回一个 ValueTask<int>,它没有缓存的概念,并且能像下面代码一样编码:

public override ValueTask<int> ReadAsync(byte[] buffer, int offset, int count)
{try {int butyRead = Read(buffer, offset, count);return new ValueTask<int>(bytesRead);}catch (Exception e){return new ValueTask<int>(Task.FromException<int>(e));}
}

ValueTask 和 异步完成

为了写一个异步方法不需要为结果类型占用额外的分配的情况下完成同步,是一个巨大的胜利。这就是为什么我们把 ValueTask<TResult> 添加到 .NET Core 2.0 的原因以及为什么我们期望去使用的新的方法返回 ValueTask<TResult> 而不是 Task<TResult>。例如,当我们添加新的 ReadAsync 重载函数到 Stream 类中是为了能够传递给 Memory<byte> 而不是 byte[],我们使它返回的类型是 ValueTask<TResult>。这样,Stream(它提供 ReadAsync 同步完成方法)和之前的 MemoryStream 的例子一样,使用这个签名(ValueTask)能够减少内存分配。

然而,工作在高吞吐的服务时,我们还是要考虑尽可能的减少分配,也就是说要考虑减少以及移除异步完成相关的内存分配。

对于 await 模式,对于所有的异步完成的操作,我们都需要能够去处理返回表示完成事件的操作的对象:调用者必须能够传递当操作完成时要被调用的回调函数以及要求有一个唯一对象能够被重用,这需要有一个唯一的对象在堆上,它能够作为特定操作的管道。但是,这并不以为这一旦这个操作完成所有关于这个对象都能被重用。如果这个对象能够被重用,那么这个 API 维护一个或多个这样对象的缓存,并且为序列化操作重用,这意思就是说不能使用相同对象到多次异步操作,但是对于非并发访问是可以重用的。

在 .NET Core 2.1,ValueTask<TResult> 增强功能支持池化和重用。而不只是包装 TResult 或 Task<TResult>,y引入了一个新的接口,IValueTaskSource<TResult>,增强 ValueTask<TResult> 能够包装的很好。IValueTaskSource<TResult> 提供必要的核心的支持,以类似于 Task<TResult> 的方式来表示 ValueTask<TResult> 的异步操作:

public interface IValueTaskSource<out TResult>
{ValueTaskSourceStatus GetStatus(short token);void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOmCompletedFlags flags);TResult GetResult(short token);
}

GetStatus 用来满足像 ValueTask<TResult>.Completed 等属性,返回一个表示异步操作是否正在执行中还是是否完成还是怎么样(成功或失败)。OnCompleted 是被 ValueTask<TResult> 的可等待者用于当操作完成时,从 await 中挂起的回调函数继续运行。GetResult 用于获取操作的结果,就像操作完成后,等待着能够获得 TResult 或传播可能发生的所有异常。

绝大多数开发者不需要去看这个接口:方法简单返回一个 ValueTask<TResult>,它被构造去包装这个接口的实例,消费者并不知情(consumer is none-the-wiser)。这个接口主要就是让开发者关注性能的 API 能够避免内存分配。

在 .NET Core 2.1 有一些这样的 API。最值得注意的是 Socket.ReceiveAsync 和 Socket.SendAsync,有新增的重载,例如

public ValueTask<int> ReceiveAsync(Momory<byte> buffer, SocketFlags socketFlags, CancellationToken cancellationToken = default);

这个重载返回 ValueTask<int>。如果这个操作同步 完成,它能构造一个 ValueTask<int> 并得到一个合适的结果。

int result = ...;
return new ValueTask<int>(result);

Socket 实现了维护一个用于接收和一个用来发送的池对象,这样每次每个完成的对象不超过一个,这些重载函数都是 0 分配的,甚至是它们完成了异步操作。然后 NetworkStream 就出现了。举个例子,在 .NET Core 2.1 中 Stream 暴露这样一个方法:

public virtual ValueTask<int> ReadAsync(Memory<byte> buffer, cancellationToken cancellationToken);

这个复写方法 NetworkStreamNetworkStream.ReadAsync 只是委托给 Socket.ReceiveAsync,所以从 Socket 转成 NetworkStream,并且 NetworkStream.ReadAsync 是高效的,无分配的。

非泛型 ValueTask

当 .NET Core 2.0 引入 ValueTask<TResult> ,它纯碎是为了优化同步完成的情况下,为了避免分配一个 Task<TResult> 存储可用的 TResult。这也就是说非泛型的 ValueTask 是不必要的:对于同步完成的情况,从 Task 返回的方法返回 Task.CompletedTask 单例,并且为 async Task 方法在运行时隐式的返回。

随着异步方法零开销的实现,非泛型 ValueTask 变得再次重要起来。因此,在 .NET Core 2.1 中,我们也引入了非泛型的 ValueTask 和 IValueTaskSource。它们提供泛型的副本版本,相同方式使用,在 void 类型使用。

IValueTaskSource / IValueTaskSource 实现

大多数开发者不需要实现这些接口。它们也不是那么容易实现的。如果你需要这么做,在 .NET Core 2.1 有一些内部实现作为参考。例如:

  • AwaitableSocketAsyncEventArgs

  • AsyncOperation

  • DefaultPipeReader

为了让开发者想做的更加简单,在 .NET Core 3.0 中,我们计划引入所有封装这些逻辑到 ManualResetValueTaskSource<TResult> 类中去,这是一个结构体,能被封装到另一个对象中,这个对象实现了 IValueTaskSource<TResult> 以及/或者 IValueTaskSource,这个包装类简单的将大部分实现委托给结构体即可。要了解更多相关的问题,详见 dotnet/corefx 仓库中的 issues。

ValueTasks 有效的消费模式

从表面上来看,ValueTask 和 ValueTask<TResult> 要比 Task 和 Task<TResult> 更加有限。没错,这个方法主要的消费就是简单的等待它们。

但是,因为 ValueTask 和 ValueTask<TResult> 可能封装可重用的对象,因此与 Task 和 Task<TResult> 相比,如果有人偏离期望的路径而只是等待它们,它们的消耗实际上受到了很大的限制。一般的,像下面的操作永远不会执行在 ValueTask / ValueTask<TResult> 上:

  • 等待 ValueTask / ValueTask<TResult> 多次。底层对象可能已经回收了并被其他操作使用。与之对比,Task / Task<TResult> 将永不会把从完成状态转成未完成状态,所以你能根据需要等待多次,并每次总是能得到相同的结果。

  • 并发等待 ValueTask / ValueTask<TResult>。底层对象期望一次只在从单个消费者的回调函数执行,如果同时等待它很容易发生条件争用以及微妙的程序错误。这也是上述操作具体的例子:“等待 ValueTask / ValueTask<TResult> 多次。”,相反,Task / Task<TResult> 支持任何数量并发的等待。

  • 当操作还没完成时调用 .GetAwaiter().GetResult()IValueTaskSource / IValueTaskSource<TResult> 的实现在操作还没完成之前是不需要支持阻塞的,并且很可能不会,这样的操作本质上就是条件争用,不太可能按照调用者的意图调用。相反,Task / Task<TResult> 能够这样做,阻塞调用者一直到任务完成。

如果你在使用 ValueTask / ValueTask<TResult> 以及你需要去做上述提到的,你应该使用它的 .AsTask() 方法获得 Task / Task<TResult>,然后方法会返回一个 Task 对象。在那之后,你就不能再次使用 ValueTask / ValueTask<TResult>

简而言之:对于 ValueTask / ValueTask<TResult>,你应该要么直接 await (可选 .ConfigureAwait(false))要么调用直接调用 AsTask(),并且不会再次使用它了。


public ValueTask<int> SomeValueTaskReturningMethodAsync();
...int result = await SomeValueTaskReturningMethodAsync();int result = await SomeValueTaskReturningMethodAsync().ConfigureAwait(false);Task<int> t = SomeValueTaskReturningMethodAsync().AsTask();ValueTask<int> vt = SomeValueTaskReturningMethodAsync();
...ValueTask<int> vt = SomeValueTaskReturningMethodAsync();
int result = await vt;
int result2 = await vt;ValueTask<int> vt = SomeValueTaskReturningMethodAsync();
Task.Run(async () => await vt);
Task.Run(async () => await vt);ValueTask<int> vt = SomeValueTaskReturningMethodAsync();
int result = vt.GetAwaiter().GetResult();

还有一个高级模式开发者可以选择使用,在自己衡量以及能找到它提供的好处才使用它。

特别的,ValueTask / ValueTask<TResult> 提供了一些属性,他们能表明当前操作的状态,例如如果操作还没完成, IsCompleted 属性返回 false ,以及如果完成则返回 true(意思是不会长时间运行以及可能成功完成或相反),如果只有在成功完成时(企图等待它或访问非抛出来的异常的结果)IsCompletedSuccessfully 属性返回 true 。对于开发者所想的所有热路径,举个例子:开发者想要避免一些额外的开销,而这些开销只在一些必要的径上才会有,可以在执行这些操作之前检查这些属性,这些操作实际上使用 ValueTask / ValueTask<TResult>,如 .await,.AsTask()。例如,在 .NET Core 2.1 中 SocketsHttpHandler 的实现,代码对连接读操作,它返回 ValueTask<int>。如果操作同步完成,那么我们无需担心这个操作是否能被取消。但是如果是异步完成的,那么在正在运行时,我们想要取消操作,那么这个取消请求将会关闭连接。这个代码是非常常用的,并且分析显示它只会有一点点不同,这个代码本质上结构如下:

int bytesRead;
{ValueTask<int> readTask = _connection.ReadAsync(buffer);if(readTask.IsCompletedSuccessfully){bytesRead = readTask.Result;}else{using(_connection.RegisterCancellation()){bytesRead = await readTask;}}
}

这种模式是可接受的,因为 ValueTask<int> 是不会在调用 .Result 或 await 之后再次使用的。

是否每个新的异步 API 都应返回 ValueTask / ValueTask ?

不!默认的选择任然还是 Task / Task<TResult>

正如上面强调的,Task / Task<TResult> 要比 ValueTask / ValueTask<TResult> 更容易正确使用,除非性能影响要大于可用性影响,Task / Task<TResult> 任然是优先考虑的。返回 ValueTask<TResult> 取代 Task<TResult> 会有一些较小的开销,例如在微基准测试中,等待 Task<TResult> 要比等待 ValueTask<TResult> 快,所以如果你要使用缓存任务(如你返回 Task / Task<bool> 的 API),在性能方面,坚持使用 Task / Task<TResult> 可能会更好。ValueTask / ValueTask<TResult> 也是多字相同大小的,在他们等待的时候,它们的字段存储在一个正在调用异步方法的状态机中,它们会在相同的状态机中消耗更多的空间。

然而,ValueTask / ValueTask<TResult> 有时也是更好的选择,a)你期望在你的 API 中只用直接 await 他们,b)在你的 API 避免相关的分配开销是重要的,c)无论你是否期望同步完成是通用情况,你都能够有效的将对象池用于异步完成。当添加 abstract,virtual,interface 方法时,你也需要考虑这些场景将会在复写/实现中存在。

ValueTask 和 ValueTask 的下一步是什么?

对于 .NET 核心库,我们讲会继续看到新的 API 返回 Task / ValueTask<TResult>,但是我们也能看到在合适的地方返回 ValueTask / ValueTask<TResult> 的 API。据其中一个关键的例子,计划在 .NET Core 3.0 提供新的 IAsyncEnuerator支持。IEnumerator<T> 暴露了一个返回 bool 的MoveNext 方法,并且异步 IAsyncEnumerator<T> 提供了 MoveNextAsync 方法。当我们初始化开始设计这个特性的时候,我们想过 MoveNextAsync 返回 Task<bool>,这样能够非常高效对比通用的 MoveNextAsync 同步完成的情况。但是,考虑到我们期望的异步枚举影响是很广泛的,并且它们都是基于接口,会有很多不同的实现(其中一些可能非常关注性能和内存分配),考虑到绝大多数的消费者都将通过 await fearch 语言支持,我们将 MoveNextAsync 改成返回类型为 ValueTask<bool>。这样就允许在同步完成场景下更快,也能优化实现可重用对象能够使异步完成更加减少分配。实际上,当实现异步迭代器时,C# 编译器就会利用这点能让异步迭代器尽可能降低分配。

原文地址:https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313525.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NET Core 3.0 AutoFac替换内置DI的新姿势

.NET Core 3.0 和 以往版本不同&#xff0c;替换AutoFac服务的方式有了一定的变化&#xff0c;在尝试着升级项目的时候出现了一些问题。原来在NET Core 2.1时候&#xff0c;AutoFac返回一个 IServiceProvider 参数注入到ConfigureServices .NET Core 服务中&#xff0c;基本大痣…

asp.net core 使用Mysql和Dapper

序曲&#xff1a;学习编程最好的方式就是敲代码&#xff0c;没有比这个更好的方法&#xff0c;哪怕你看了上百G的视频&#xff0c;都不如你自己敲几行代码更为有效。还有要记得敲完代码然后写一篇随笔来记录一下你所学所想。大家都知道&#xff0c;.netcore是微软一个具有历史意…

CSFR(跨站请求伪造)攻击与防御

一、CSRF是什么&#xff1f; CSRF&#xff08;Cross-site request forgery&#xff09;&#xff0c;中文名称&#xff1a;跨站请求伪造&#xff0c;也被称为&#xff1a;one click attack/session riding&#xff0c;缩写为&#xff1a;CSRF/XSRF。 二、CSRF可以做什么&#…

Vue 3源码公布

10 月 5 日凌晨&#xff0c;Vue.js 框架的作者兼核心开发者尤雨溪公布了尚处于 Pre-Alpha 状态的 Vue 3 源码。说学不动的童鞋抓紧剩余的假期时间撸一遍源码吧 : D作者表示&#xff0c;Vue 3 主要的架构改进、优化和新功能均已完成&#xff0c;剩下的主要任务是完成一些 Vue 2 …

在副业刚需的时代,如何掌握副业的正确姿势?

前言近期&#xff0c;伴随着“副业刚需”这个词语的流行&#xff0c;关于“职场人要不要发展副业”的话题再一次被炒得沸沸扬扬。有人认为副业是刚需&#xff0c;是抵御中年危机的锦囊妙计&#xff0c;甚至是中年人该有的自觉&#xff0c;没有副业的人不足以谈人生&#xff0c;…

使用.NET Core创建Windows服务(二) - 使用Topshelf方式

原文&#xff1a;Creating Windows Services In .NET Core – Part 2 – The “Topshelf” Way作者&#xff1a;Dotnet Core Tutorials译者&#xff1a;Lamond Lu译文&#xff1a;使用.NET Core创建Windows服务&#xff08;二&#xff09; - 使用Topshelf方式使用.NET Core创建…

常用加密算法(Java实现)总结

1、Java的安全体系架构 Java中为安全框架提供类和接口。JDK 安全 API 是 Java 编程语言的核心 API&#xff0c;位于 java.security 包&#xff08;及其子包&#xff09;&#xff0c;以及sun.securityAPI包&#xff08;及其子包&#xff09;中。设计用于帮助开发人员在程序中同…

怎样的项目才能称为“成功项目”?

作者&#xff1a;邹溪源&#xff0c;长沙资深互联网从业者&#xff0c;架构师社区合伙人&#xff01;引子这个故事讲的是一家拥有百年历史的制造业大厂的信息化转型过程中的波折。这家企业拥有超过三万名员工&#xff0c;它是某行业的领先品牌&#xff0c;但是在信息化程度上却…

彩虹表

一、简介 彩虹表就是一个庞大的、针对各种可能的字母组合预先计算好的哈希值的集合&#xff0c;不一定是针对MD5算法的&#xff0c;各种算法的都有&#xff0c;有了它可以快速的破解各类密码。越是复杂的密码&#xff0c;需要的彩虹表就越大&#xff0c;现在主流的彩虹表都是1…

深入Dapper.NET源码

经过业界前辈、StackOverflow多年推广,「Dapper搭配Entity Framework」成为一种功能强大的组合,它满足「安全、方便、高效、好维护」需求。但目前中文网路文章,虽然有很多关于Dapper的文章但都停留在如何使用,没人系统性解说底层原理。所以有了此篇「深入Dapper源码」想带大家进…

DDoS攻击与防御

一、DDOS介绍 要了解DDOS攻击是什么&#xff0c;首先要了解DOS攻击的基本原理是至关重要的。 DoS攻击是最早出现的&#xff0c;它的攻击方法说白了就是单挑&#xff0c;是比谁的机器性能好、速度快。但是现在的科技飞速发展&#xff0c;一般的网站主机都有十几台主机&#xf…

DRDoS(memcache漏洞导致的反射型分布式拒绝服务攻击)

一、DDoS基础 见博文&#xff1a;DDoS攻击与防御 二、Memcached 反射DDOS攻击原理 反射DDOS是发送大量带有被害者IP地址的请求给反射服务器&#xff0c;反射服务器对IP地址源做出大量回应&#xff0c;形成拒绝服务攻击。CLOUDFLARE的这张图很好的解释了DDOS反射攻击过程&…

田牌魔术 | .NET Core 3.0 + Azure 远程点亮树莓派上的一盏灯

点击上方蓝字关注“汪宇杰博客”导语3年前&#xff0c;我写过一篇《Windows 10 IoT Core Azure 远程控制LED》&#xff0c;实现了《生活大爆炸》中的注孤生实验&#xff0c;让信号从家里出发&#xff0c;绕地球转一圈&#xff0c;经过微软美国数据中心&#xff0c;返回家里点亮…

使用Hash碰撞进行DoS攻击

一、哈希表碰撞攻击的基本原理 哈希表是一种查找效率极高的数据结构&#xff0c;很多语言都在内部实现了哈希表。PHP中的哈希表是一种极为重要的数据结构&#xff0c;不但用于表示Array数据类型&#xff0c;还在Zend虚拟机内部用于存储上下文环境信息&#xff08;执行上下文的…

EF Core 实现读写分离的最佳方案

前言公司之前使用Ado.net和Dapper进行数据访问层的操作, 进行读写分离也比较简单, 只要使用对应的数据库连接字符串即可. 而最近要迁移到新系统中,新系统使用.net core和EF Core进行数据访问. 所以趁着国庆假期拿出一两天时间研究了一下如何EF Core进行读写分离.思路根据园子里…

SSL工作原理

本文介绍了SSL原理、安全机制、工作过程和典型网络应用。 缩略语列表 一、概述 1.1 产生背景 基于万维网的电子商务和网上银行等新兴应用&#xff0c;极大地方便了人们的日常生活。受到人们的青睐。 因为这些应用都须要在网络上进行在线交易&#xff0c;它们对网络通信的…

在 ASP.NET Core 项目中使用 AutoMapper 进行实体映射

一、前言在实际项目开发过程中&#xff0c;我们使用到的各种 ORM 组件都可以很便捷的将我们获取到的数据绑定到对应的 List<T> 集合中&#xff0c;因为我们最终想要在页面上展示的数据与数据库实体类之间可能存在很大的差异&#xff0c;所以这里更常见的方法是去创建一些…

.NET开发者必须学习.NET Core

很多的.NET开发者在接触.Net Core之前&#xff0c;对于linux系统一点也不了解&#xff0c;也未曾有过主动去学习的念头。在接触了.Net Core之后才会慢慢学习linux相关知识&#xff0c;很多同学想转Java&#xff0c;这个很扎心&#xff0c;你有很好的条件转向.NET Core为啥要转J…

Java事务管理

事务的ACID属性&#xff1a;原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation)和持久性(Durabilily) ACID 特性 A&#xff08;原子性&#xff09;事务的原子操作单元&#xff0c;对数据的修改&#xff0c;要么全部执行&#xff0c;要么全部不执行&#x…

如何提高QnA maker机器人训练中文语义理解的能力

这是一个常见的问题&#xff0c;在人工智能的世界里面&#xff0c;图像理解、语言及语义理解、数据理解是三个核心领域。而关于语言及语义理解&#xff0c;又与具体的语言和文字密切相关。目前来说&#xff0c;大家都是用机器学习去训练模型&#xff0c;如果要更好的理解中文&a…