[C#]7.0為async新增的ValueTask的作用

[C#]7.0為async新增的ValueTask的作用

之前筆者有寫過一系列關於非同步的文章,而此篇文章就要來分析為何要出現ValueTask,使用時機點為何呢?我們都知道每種功能和語法,都是為了某些需求或情境出現的,先來了解一下async task這種方式,如果我們以如下的方式實做await的話,就會造成效能低落,還不如不要使用async方法。

var msgs=new List<string>();
foreach (var msg in msgs)
{
	await SomeMethodAsync(); 
	//do something
}

async Task<string> SomeMethodAsync(){
	await Task.Delay(1);
	return "Hello";
}

那上面的語法又為何會造成效能低落呢?因為Task其實是一個參考物件,每次要使用Task的時候就會造成heap allocation(內存分配),當我們在迴圈反覆使用的話,就會造成很多效能上的損耗,我們都知道C#的GC自動回收很方便,也最大化的自動管理了記憶體上的配置,不過其實GC還是會造成一點損耗的。

ValueTask是C#7.0之後才有的,所以我們必須從nuget下載安裝

那ValueTask則是實值型別的,所以只會存放在Stack裡面,只會存放內容值而不是記憶體位址,所以我們如果把之前的例子改成如下就沒什麼大問題了

var msgs=new List<string>();
foreach (var msg in msgs)
{
	await SomeMethodAsync(); 
	//do something
}

async ValueTask<string> SomeMethodAsync(){
	await Task.Delay(1);
	return "Hello";
}

在看另一個例子,如下程式碼,其實根本就不會跑到await陳述式,條件成立就回傳0了,但因為我們使用了Task,所以預設已經產生了Task物件

int res = CalculateSum(0, 0).Result;
Console.WriteLine(res);

async Task<int> CalculateSum(int a, int b)
{
	if (a == 0 && b == 0)
	{
		return 0;
	}

	return await Task.Run(() => a + b);
}

此時一樣的我們只要使用ValueTask來取改掉Task,就不會造成無謂的內存分配了

int res = CalculateSum(0, 0).Result;
Console.WriteLine(res);

async ValueTask<int> CalculateSum(int a, int b)
{
	if (a == 0 && b == 0)
	{
		return 0;
	}

	return await Task.Run(() => a + b);
}