3

我花了差不多兩天的時間閱讀異步/等待教程和答案Stackoverflow和試圖瞭解C#中的異步和並行執行。儘管如此,我還是無法讓它與我的代碼一起工作。InvalidCastException當使用任務返回值

我需要什麼

執行通過PrincipalSearcher一個Active Directory搜索異步無阻塞的WPF界面。

實施

_activeDirectory
protected async void SearchButtonClick() 
{ 
    Task<PrincipalSearchResult<Principal>> searchTask = Task.Run(() => _activeDirectory.FindGroup(searchText.Text)); 

    PrincipalSearchResult<Principal> searchResult = await searchTask; 

    foreach (var foundGroup in searchResult) /*exception thrown here*/ 
    { 
     ... 
    } 
} 

public PrincipalSearchResult<Principal> FindGroup(String pattern) 
{ 
    ... 
    PrincipalSearchResult<Principal> searchResult = searcher.FindAll(); 
    return searchResult; 
} 

問題

  • await似乎並沒有等待完成任務。 searchTask.IsCompleted在等待線之後是真實的,但它不能是因爲幾乎沒有時間完成,如果我同步運行它,搜索需要大約5秒。
  • 如果在foreach循環的開始拋出的異常:

System.InvalidCastException occurred HResult=-2147467262
Message=Unable to cast COM object of type 'System.__ComObject' to interface type 'IDirectorySearch'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{109BA8EC-92F0-11D0-A790-00C04FD8D5A8}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)). Source=System.DirectoryServices StackTrace: at System.DirectoryServices.SearchResultCollection.get_SearchObject()
InnerException:

思考

  • 我發現的是,這種異常的無效的SynchronizationContext是相關的,但我不知道看看這可能發生在這裏。我還在幾行代碼上打印了Thread.CurrentThread.ManagedThreadId,它總是返回相同的ID。
  • 我也讀過,異步方法不應該返回voidTask<T>,但我不認爲這是相關的,因爲沒有人異步使用SearchButtonClick()。而且如果這個問題甚至相關的話,Task.Run()可能會返回一個Task。整個主題對我來說依然模糊不清。

問題

  • 爲什麼await不會等待任務完成?
  • 異常的原因是什麼?
+0

http://stackoverflow.com/questions/18110738/passing-collection-from-backgroundworker-dowork-to-backgroundworker-completed-an –

+0

@Gosha_Fighten我已經試過'PrincipalSearchResult 信息搜索結果=(PrincipalSearchResult )等待searchTask ;'。這並沒有解決問題。 – mrplow

回答

2

你不能在另一個線程比,你創建對象使用_activeDirectory

這是基於在對象內部使用COM主機的方式實現的。有些COM主機是以一種方式實現的,以便它們可以在多個線程上使用,有些以一種方式實現,以便只能在創建它的同一個線程上使用對象。

您需要更改代碼以在Task.Run中執行的代碼中創建_activeDirectory,或更改_activeDirectory的實現以創建用於訪問搜索方法內部活動目錄的COM對象。

您還需要確保線程有ApartmentStateApartmentState.STA。有關如何做到這一點,請參閱this question


由於異常立即返回等待的調用。如果任務中發生未處理的異常,那麼該任務在那一刻完成。

執行搜索並在同步執行時需要5秒,因爲如上所述,在與創建它的同一線程中使用COM對象時不會發生異常。

3

await確實等待Task完成。 async\await模式和TPL庫錯過的唯一一件事是Exception被拋出之內Task在那一刻沒有被拋出。它被緩存在TaskException屬性中,並且在你想獲得結果之後立即拋出。

所以問題在於你在Task內運行的代碼不能運行到另一個線程中,就像@NineBerry所說的那樣。你必須創建AD -object Task,像這樣:

Task<PrincipalSearchResult<Principal>> searchTask = Task.Run(() => 
{ 
    // this have to be a local variable inside your task 
    var _activeDirectory = GET_THE_AD(); 
    return _activeDirectory.FindGroup(searchText.Text)); 
} 

,或者可能是,你必須爲你的線程searcher變量各一次。