4

我創建了一個活動來執行Web請求並將結果存儲到數據庫中。通常這個過程大約需要1個小時,並且會導致工作流引擎出現異常。我發現對於這些長時間運行的活動,我應該編寫一些不同的代碼,以便工作流引擎線程不會被阻塞。使用TPL處理WF 4.0長時間運行活動

學習一些關於編寫長時間運行活動的博客我明白我應該使用Bookmark概念。但我沒有使用TPL和Task的任何解決方案。

此代碼是否正確使用Task s來處理長時間運行的活動?

public sealed class WebSaveActivity : NativeActivity 
{ 
    protected override void Execute(NativeActivityContext context) 
    { 
     context.CreateBookmark("websave", (activityContext, bookmark, value) => 
     { 

     }); 

     Task.Factory.StartNew(() => 
     { 
      GetAndSave(); // This takes 1 hour to accomplish. 
      context.RemoveBookmark("websave"); 
     }); 

    } 

    protected override bool CanInduceIdle 
    { 
     get 
     { 
      return true; 
     } 
    } 
} 
+0

'StartNew'應謹慎使用,請考慮使用'Run' – VMAtm

回答

1

我剛纔看到您的相關問題在這裏:How to write a long running activity to call web services in WF 4.0

另一種方式是實現您的活動是作爲一個AsyncCodeActivity

namespace MyLibrary.Activities 
{ 
    using System; 
    using System.Activities; 

    public sealed class MyActivity : AsyncCodeActivity 
    { 
     protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state) 
     { 
      var delegateToLongOperation = new Func<bool>(this.LongRunningSave); 
      context.UserState = delegateToLongOperation; 
      return delegateToLongOperation.BeginInvoke(callback, state); 
     } 

     protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result) 
     { 
      var longOperationDelegate = (Func<bool>) context.UserState; 
      var longOperationResult = longOperationDelegate.EndInvoke(result); 

      // Can continue your activity logic here. 
     } 

     private bool LongRunningSave() 
     { 
      // Logic to perform the save. 
      return true; 
     } 
    } 
} 

工作流實例停留在內存中,但至少工作流運行時可以處理其正常的調度任務,而不需要它的線程被長時間運行的進程佔用。

3

不,這不是應該使用書籤的方式。當工作流必須等待來自外部進程的輸入時,使用書籤。

例如:我有一個文檔審批工作流程,並且在某個時間點,工作流程必須等待人員審閱者對該文檔給出確認。當調用ResumeBookmark時,工作流程將被閒置並由運行時再次激活,而不是將工作流程實例保存在內存中。

在您的情況下,您的工作流程無法閒置,因爲它具有在其上下文中運行的操作。順便說一下,這個操作是你的任務,是一個不間斷的任務,因此WF無法處理嚴重的故障。

現在,對於一種可能的解決方案,您可能會考慮讓另一個進程調用GetAndSave方法,並讓該進程最終在WF上調用ResumeBookmark,這樣工作流可以由運行時空閒。這個過程甚至可以是承載您的工作流程的相同過程。

有關示例,請參閱this blogpost。只是想象一下,不是等待人類在控制檯中輸入某些東西,而是執行長時間運行的任務。

您沒有指定在您的活動之後發生了什麼,但請注意,恢復書籤時可以將數據返回到工作流。因此,任何GetAndSave的結果,即使它只是一個錯誤代碼,您可以用它來決定如何進一步處理工作流程中的其他活動。

希望這對你有意義,你會看到我試圖概述的可能解決方案。

編輯 有關在WF中使用任務或異步/等待的快速註釋。有AFAIK沒有方法來覆蓋返回的任務,所以你必須使用.Wait().Result或者忘記它們來阻止它們。因爲如果您無法等待他們,那麼在執行工作流程時會發生不好的事情,因爲其他活動可能會在使用任務完成其工作之前啓動。

當開發WF運行時,任務的整個概念還很年輕,所以WF運行時沒有/不適合他們。

編輯2: 示例實現(基於this excellent official documentation

您的活動將是幾乎是空的:

public sealed class TriggerDownload : NativeActivity<string> 
{ 
    [RequiredArgument] 
    public InArgument<string> BookmarkName { get; set; } 

    protected override void Execute(NativeActivityContext context) 
    { 
     // Create a Bookmark and wait for it to be resumed. 
     context.CreateBookmark(BookmarkName.Get(context), 
      new BookmarkCallback(OnResumeBookmark)); 
    } 

    protected override bool CanInduceIdle 
    { 
     get { return true; } 
    } 

    public void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object obj) 
    { 
     // When the Bookmark is resumed, assign its value to 
     // the Result argument. (This depends on whether you have a result on your GetData method like a string with a result code or something) 
     Result.Set(context, (string)obj); 
    } 
} 

它標誌着工作流運行時的工作流程可以閒置,以及它如何恢復。現在

,工作流運行時配置:

WorkflowApplication wfApp = new WorkflowApplication(<Your WF>); 

// Workflow lifecycle events omitted except idle. 
AutoResetEvent idleEvent = new AutoResetEvent(false); 


wfApp.Idle = delegate(WorkflowApplicationIdleEventArgs e) 
{ 
    idleEvent.Set(); 
}; 

// Run the workflow. 
wfApp.Run(); 

// Wait for the workflow to go idle before starting the download 
idleEvent.WaitOne(); 

// Start the download and resume the bookmark when finished. 
var result = await Task.Run(() => GetAndSave()); 
BookmarkResumptionResult result = wfApp.ResumeBookmark(new Bookmark("GetData"), result); 

// Possible BookmarkResumptionResult values: 
// Success, NotFound, or NotReady 
Console.WriteLine("BookmarkResumptionResult: {0}", result); 
+0

謝謝您的回答,那麼您對編寫此類活動有什麼建議? – mehrandvd

+0

此方法需要修改我無法訪問的工作流應用程序。我正在尋找解決方案來處理我的活動中所有必需的代碼,而不是修改服務器。 – mehrandvd

+0

啊錯過了這個問題中的關鍵部分:-)無論如何,書籤或主機通信等所有內建機制都不存在。異步/等待僅僅是不受支持的,因爲你不能使用WF中的任務而沒有使用阻塞等待它們的完成,將不可能創建需要很長時間但不阻塞工作流線程的活動。 –

相關問題