[000][Concurrency in C# CookBook] - Await執行時,ConfigureAwait(False)另開context(記憶體)增加效能並防止deadlock(限定library function)

  • 746
  • 0
  • 2015-11-23

摘要:[Concurrency]ConfigureAwait(False)另開context的功用

ConfigureAwati(False)的主要功用是,讓async非同步的 "library" function的await不用caller的context(記憶體)而是用自己的context去儲存執行的狀態,是為了增加concurrency效能而存在,並且能防止不當的同步呼叫library function導致的deadlock,注意:Stephen強調,ConfigureAwait(False)一定只能加在library function,不可加在User interface thread(例如ooxx_click()裡面就絕對不能加)。

StephenCleary指出, 通常是caller端的async + await以及library function端的ConfigureAwait(False)可以達成最大的concurrency效能,單獨做其中一項的話,concurrency效能都沒那麼好。

首先是會產生deadlock的例子,流程是這樣的:

caller在click之後直接進入DoSomethingAsync之後,將記錄執行狀態的context交給DoSomethingAsync裡面的await Task.Delay(TimeSpan.FromSeconds(3));使用, 而await Task.Delay(TimeSpan.FromSeconds(3));因為是非同步執行,所以不會立刻執行完畢(3秒之後才執行完),因此記錄下目前的執行狀態到caller的context(因為目前caller與library function共用context)之後,將執行控制權還給caller(在這個例子中,第二個await Task.Delay(TimeSpan.FromSeconds(3));是執行不到的),一直到caller端執行完畢string cde = "";,這時候Page.ClientScript會用到DoSomethingAsync的結果,因此不得不到Context檢查一下是否DoSomethingAsync執行完畢了沒,因此caller是以同步的方式在執行中,因此就死捏著context資源不放,過了3秒之後,第一個await Task.Delay(TimeSpan.FromSeconds(3));總算執行完了,library function想要繼續後面的val *= 2;,因此必須先到context讀取回之前的執行狀態才能繼續執行下去,但是caller死捏著不放,因此就變成caller等library function執行完,library function也等caller執行完,就變成deadlock了。

.aspx:


<asp:Button ID="btnSyncWithoutConfigureAwait" runat="server" Text="SyncWithoutConfigureAwait" OnClick="btnSyncWithoutConfigureAwait_Click" />

code_behind:


//此例會造成Deadlock        
//ps.以同步的方式執行非同步的function是非常不建議的
protected void btnSyncWithoutConfigureAwait_Click(object sender, EventArgs e)
{            
	Task tVal = DoSomethingAsync();
	string abc = "";
	string cde = "";
	Page.ClientScript.RegisterStartupScript(this.GetType(), "", "alert('" + tVal.Result.ToString() + "');", true);
}

private async Task DoSomethingAsync()
{
	int val = 13;

	// Asynchronously wait 3 second.
	//只是為了讓此library function為非同步執行才用Task.Delay,實際尚無特別意義
	await Task.Delay(TimeSpan.FromSeconds(3));

	val *= 2;

	// Asynchronously wait 3 second.
	await Task.Delay(TimeSpan.FromSeconds(3));
	
	return val;
}

修改上例子之後,在library function加入ConfigureAwait(false)就不會造成deadlock了,因為library function另開記憶體儲存context了,如下:

.aspx:


<asp:Button ID="btnSyncWithConfigureAwait" runat="server" Text="SyncWithConfigureAwait" OnClick="btnSyncWithConfigureAwait_Click" />

code_behind:


protected void btnSyncWithConfigureAwait_Click(object sender, EventArgs e)
{
	Task tVal = DoSomethingAsyncWithConfigureAwait();
	string abc = "";
	string cde = "";
	Page.ClientScript.RegisterStartupScript(this.GetType(), "", "alert('" + tVal.Result.ToString() + "');", true);
}

//.ConfigureAwait(false)另開了context以儲存library function的執行狀態        
private async Task DoSomethingAsyncWithConfigureAwait()
{
	int val = 13;

	// Asynchronously wait 3 second.     
	//增加了.ConfigureAwait(false)另開了context
	await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(false);

	val *= 2;

	// Asynchronously wait 3 second.
	//增加了.ConfigureAwait(false)另開了context
	await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(false);

	return val;
}

最後仍要補充一下,Stephen強調,caller以非同步的方式呼叫加上ConfigureAwait(false)的非同步的library function才是最推薦的,雖然不論是library function單獨加上ConfigureAwait(False)或是單獨caller端以非同步的await執行library function都可以解決deadlock,但是兩者並行才可取得最大的concurrency效能, 如下:

ps.Stephen也強調,ConfigureAwait(false)一定只能加在library function, 千萬不要加在caller thread(例如UI端的程式碼ooxx_click()裡面絕對不能加)

.aspx


<asp:Button ID="btnAsyncWithConfigureAwait" runat="server" Text="AsyncWithConfigureAwait" OnClick="btnAsyncWithConfigureAwait_Click" />

code_behind:


protected async void btnAsyncWithConfigureAwait_Click(object sender, EventArgs e)
{
	int intVal = await DoSomethingAsyncWithConfigureAwait();
	string abc = "";
	string cde = "";
	Page.ClientScript.RegisterStartupScript(this.GetType(), "", "alert('" + intVal.ToString() + "');", true);
}

//DoSomethingAsyncWithConfigureAwait跟上面一樣,所以就不列出了

ConfigureAwait(false)介紹,大概是這樣

完整程式碼看這:

http://saltsourcecenter.blogspot.tw/2015/10/configureawaitaspx.html

 

參考書籍:

Concurrency in C# Cookbook

http://shop.oreilly.com/product/0636920030171.do

參考文章:

Don't Block on Async Code

http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html