我有一個MVVM應用程序調用數據服務來獲取一些要綁定的數據。數據服務通過實體框架6訪問SQL Server Compact(v4.0)數據庫。使用實體框架異步方法和SQL Server Compact阻止行爲
數據(當前)需要幾秒鐘才能加載,並在同步調用時(不奇怪)阻止GUI線程。大部分我都假定爲IO綁定,因此我添加了一個等效的Async方法以異步方式執行數據加載,以使GUI保持響應。但是,當我使用EF Async方法時,它看起來沒有什麼區別,GUI線程仍然阻塞,時間大致相同。
我明白,使用異步調用不會將工作移動到不同的線程,但我認爲這個活動的大部分是IO綁定。因此,使用EF異步方法應該允許調用(GUI)線程繼續執行其他工作,但它沒有任何區別。
例如,如果我寫使用EF同步Load方法LoadData方法,則GUI塊,直到數據被加載:
public ObservableCollection<Data> GetData()
{
//Do IO bound activity...
Context.DataTable1.Include(...).Load();
//for demo purposes, just return some data
return Context.DataTable1.Local; //(ObservableCollection<Data>)
}
的異步方法中加載數據看起來像這樣:
public async Task<ObservableCollection<Data>> GetDataAsync()
{
//Do IO bound activity...
var task = Context.DataTable1.Include(...).LoadAsync();
await task;
//for demo purposes, just return some data
return Context.DataTable1.Local; //(ObservableCollection<Data>)
}
令人驚訝的是(對我來說)我得到了相同的結果,它阻塞了調用線程大致相同的時間長度(我把一個秒錶)。
我開始認爲除了數據庫IO綁定工作之外,可能還有一些導致阻塞的CPU綁定活動的最小數量。於是,我終於嘗試用Task.Run()執行在後臺線程的工作:
public async Task<ObservableCollection<Data>> GetDataAsync()
{
//Do IO bound activity...
Task<ObservableCollection<Competition>> task = Task.Run(async() =>
{
//Do IO bound activity...
var task = Context.DataTable1.Include(...).LoadAsync();
await task;
//for demo purposes, just return some data
return Context.DataTable1.Local; //(ObservableCollection<Data>)
});
var result = await task;
return result;
}
這樣,GUI顯然不會阻止和響應的整個時間。
我讀過圍繞這一切的文章很多,包括什麼時候不(here),何時(here)使用Task.Run職位here和here和博客文章由斯蒂芬·克利裏。我知道我上面的最後一個示例仍然是阻塞的,它只是阻塞了一個線程池線程。我不明白的是,爲什麼在訪問EF異步方法時看起來沒有提供任何好處?
難道是在IO活動正在進行時,還有足夠的CPU綁定工作來導致阻塞?它肯定需要比50ms長得多的響應時間,接近2或3秒才能運行所有查詢,所以這種類型的活動可以開始證明Task.Run的使用是正確的。
或者,我是否寫錯了Async方法,爲什麼它仍然阻塞?
我也想知道是否EF的SQL壓縮提供程序的異步方法是由於某種原因回落到標準的同步調用?
相關:http:// stackoverflow。COM /問題/ 27051169 /爲什麼 - 不 - 等待 - loadasync進行冷凍的-UI-同時,等待任務運行負荷。並非所有的異步方法都是真正的異步方法。它可以做一些微不足道的阻塞操作,有時候會阻止呼叫者很長時間(由於任何原因)。例如'webclient.downloadstringtaskasync'可能會被阻止,因爲它會同步執行Dns查找AFAICR。 – 2015-02-23 14:11:31
SQL Server Compact ADO.NET提供程序不提供任何Async API。但是,加載數據不應該花費幾秒鐘,除非您獲取1000行。在啓動應用程序時初始化dbContext.connection對象,並在應用程序的整個生命週期中保留爲空且未使用。首次使用dbContext初始化的成本也是如此。 – ErikEJ 2015-02-23 16:11:46
感謝@ErikEJ的確認,鑑於我看到的行爲,我想知道是否是這種情況。如果您想將其設置爲答案,我會將其標記爲已回答。 – Jason 2015-02-23 20:34:19