2015-11-04 71 views
1

我聲明瞭一個應該異步運行Task的方法,然後當Task完成時,同步運行指定的代理如何同步運行一個延續任務?

C#例如:

private void WaitUntilLoadedAsync(Process p, Action callback, int timeout = 1500) 
{ 
    Task.Factory.StartNew(() => ProcessUtil.WaitUntilLoaded(p, timeout)). 
       ContinueWith(() => callback.Invoke()).RunSynchronously(); 

} 

VB.Net示例:

Private Sub WaitUntilLoadedAsync(ByVal p As Process, 
           ByVal callback As Action, 
           Optional ByVal timeout As Integer = 1500) 

    Task.Factory.StartNew(Sub() ProcessUtil.WaitUntilLoaded(p, timeout)). 
       ContinueWith(Sub() callback.Invoke()).RunSynchronously() 

End Sub 

然而,RunSynchronously方法引發System.InvalidOperationException異常特靈我說的延續Task不能synchronouslly跑去。

那麼,我該怎麼做呢?

說明(S):

  • 我能夠支持Async/Await解決方案。

  • 我會保留我的方法的「微小」邏輯或圈複雜度,我的意思是,這只是一種方法,我通過一個Acton,沒有委託在方法或其他可能導致最終用戶寫入的數量多於Action。該方法本身應該使所有必需的操作自動化,以同步執行該繼續任務,而不需要外部的複雜性。

+0

當你說「同步」時,你的意思是與第一項任務有關嗎?或者與'WaitUntilLoadedAsync'方法有關? –

+0

@Yacoub馬薩德感謝您的評論。在代碼塊中有兩個任務,第一個任務調用** ProcessUtil.WaitUntilLoaded **方法,第二個任務(延續任務)調用指定的** Action **。當第一個任務異步完成時,我將同步調用** Action **。 – ElektroStudios

+0

是的,但在這種情況下意味着什麼?繼續任務將只在原始任務完成後運行。所以如果你想讓它與第一個任務同步,那麼你已經擁有它,而不需要'RunSynchronously'調用。 –

回答

2

所以,根據我對問題和意見的理解,您希望在線程池線程(後臺線程)上運行任務1,並且要運行任務2(這是任務1的延續) UI線程。

這裏是你如何能做到這:

private void WaitUntilLoadedAsync(Process p, Action callback, int timeout = 1500) 
{ 
    var thread_scheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

    Task.Factory.StartNew(() => ProcessUtil.WaitUntilLoaded(p, timeout)). 
     ContinueWith(t => callback.Invoke(), CancellationToken.None, TaskContinuationOptions.None, 
      thread_scheduler); 
} 

這段代碼是幹什麼的是,它要求TPL對將在UI線程執行任務調度運行延續任務。

這裏假定從UI線程調用WaitUntilLoadedAsync。如果不是這種情況,只需將thread_schedular作爲一個實例變量(或者在可從此方法訪問的某個範圍內),並確保從UI線程對其進行初始化。

請注意,控件將立即返回給調用者WaitUntilLoadedAsync,並且任務1完成後,將在UI線程上執行回調。

如果要執行只有WaitUntilLoadedAsync從UI線程調用UI線程上的後續任務(和上一線程池線程執行它,否則),然後定義thread_scheduler如下所示:

var thread_scheduler = SynchronizationContext.Current == null 
    ? TaskScheduler.Default 
    : TaskScheduler.FromCurrentSynchronizationContext(); 
+0

顯示的過載差異與您的解決方案相關,以儘可能地保證安全,如果我試圖從非UI線程運行它,請確認此條件是否可以確保方法邏輯安全,以避免發生'InvalidOperationException'? 'if(System.Windows.Forms.Application.MessageLoop){toscheduler = TaskScheduler。{0} {0} {0}FromCurrentSynchronizationContext(); } else { \t tScheduler = TaskScheduler.Current; }' – ElektroStudios

+0

你的應用程序是一個UI應用程序嗎?例如它是一個Windows窗體應用程序?你總是希望繼續任務在UI線程上運行嗎?或者任何調用'WaitUntilLoadedAsync'的線程? –

+0

這是一個基於WinForms技術的UI應用程序,但我假裝將該方法添加到可以「無處不在」執行的庫(dll程序集)中,我希望延續任務僅在UI線程上運行,只要** WaitUntilLoadedAsync * *是從UI線程啓動的,這是否可以使用該條件?到目前爲止,當我從UI或非UI線程啓動該方法時沒有對該條件進行發佈,但我會確保。 – ElektroStudios

2

聽起來像是你正在尋找TaskContinuationsOptions.ExecuteSynchronously

MSDN

指定的後續任務應同步執行。通過指定此選項,延續運行在導致先行任務轉換到其最終狀態的同一線程上。如果在創建延續時先行詞已經完成,則延續將在創建延續的線程上運行。如果先行者的CancellationTokenSource放置在finally塊中(最後在Visual Basic中),則使用此選項的延續將在該finally塊中運行。只有非常短的延續應該同步執行。

因爲任務是同步執行的,所以不需要調用諸如Task.Wait之類的方法來確保調用線程等待任務完成。

要使用它只是將它作爲標誌使用過載.ContinueWith(Action<task>, TaskContinuationOptions)

然而,這將不會是同步的,其中,如果所執行的父被中止該線程(這是不常見)。

+0

謝謝這麼多,但我發現,重載不會導致繼續任務被運行在** WaitUntilLoadedAsync **的調用者,在由@Yacoub Massad – ElektroStudios