2015-08-19 57 views
1

我一直在嘗試通過創建示例應用程序來了解TPL數據流。我一直在試圖做的一件事是從ActionBlock更新TextBox控件。使用TPL Dataflow的原因是在保持順序的同時執行並行異步操作。以下函數由我自己編寫,從ActionBlock更新UI控件

private TaskScheduler scheduler = null; 

public Form1() 
    { 
     this.scheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
     InitializeComponent(); 
    } 

public async void btnTPLDataFlow_Click(object sender, EventArgs e) 
    { 
     Stopwatch watch = new Stopwatch(); 
     watch.Start(); 

     txtOutput.Clear(); 

     ExecutionDataflowBlockOptions execOptions = new ExecutionDataflowBlockOptions(); 
     execOptions.MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded; 
     execOptions.TaskScheduler = scheduler; 

     ActionBlock<int> actionBlock = new ActionBlock<int>(async v => 
     { 
      bool x = await InsertIntoDatabaseAsync(v); 

      if (x) 
       txtOutput.Text += "Value Inserted for: " + v + Environment.NewLine; 
      else 
       txtOutput.Text += "Value Failed for: " + v + Environment.NewLine; 

     }, execOptions); 


     for (int i = 1; i <= 200; i++) 
     { 
      actionBlock.Post(i); 
     } 

     actionBlock.Complete(); 
     await actionBlock.Completion;    

     watch.Stop(); 
     lblTPLDataFlow.Text = Convert.ToString(watch.ElapsedMilliseconds/1000); 
    } 


private async Task<bool> InsertIntoDatabaseAsync(int id) 
    { 
     try 
     { 
      string connString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\\TPLDatabase.accdb;Persist Security Info=False;"; 

      using (OleDbConnection conn = new OleDbConnection(connString)) 
      { 
       string commandText = "INSERT INTO tblRecords (ProductName, ProductDescription, IsProcessed) VALUES (@ProductName, @ProductDescription, @IsProcessed)"; 

       await conn.OpenAsync(); 
       using (OleDbCommand command = new OleDbCommand(commandText, conn)) 
       { 
        command.CommandType = CommandType.Text; 

        command.Parameters.AddWithValue("@ProductName", "Product " + id); 
        command.Parameters.AddWithValue("@ProductDescription", "Description " + id); 
        command.Parameters.AddWithValue("@IsProcessed", false); 

        if (await command.ExecuteNonQueryAsync() > 0) 
         return true; 
        else 
         return false; 
       } 
      } 
     } 
     catch 
     { 
      return false; 
     } 
    } 

現在上面這段代碼運行得很好。它按順序在我的示例MS Access數據庫中插入記錄,並按順序更新UI。但與此問題是,它阻止了用戶界面,這是可以理解的,因爲我正在使用將在UI線程上更新TextBoxTaskScheduler.FromCurrentSynchronizationContext

我在代碼中做了一些小改動,並從ExecutionDataflowBlockOptions中刪除了調度程序。相反,我用下面的代碼更新UI,

txtOutput.Invoke(new MethodInvoker(delegate 
      { 
       if (x) 
        txtOutput.Text += "Value Inserted for: " + v + Environment.NewLine; 
       else 
        txtOutput.Text += "Value Failed for: " + v + Environment.NewLine; 
      })); 

現在,這個變化不會凍結UI,但被嚴重擾亂,在文本框中顯示的值的數據庫和訂單值的順序。新順序是這樣的,

ID ProductName ProductDescription IsProcessed 
6847 Product 6  Description 6  False 
6848 Product 7  Description 7  False 
6849 Product 8  Description 8  False 
6850 Product 10 Description 10  False 
6851 Product 11 Description 11  False 
6852 Product 12 Description 12  False 
6853 Product 9  Description 9  False 
6854 Product 13 Description 13  False 
6855 Product 14 Description 14  False 

現在什麼都可以更新我的方案的UI,並且還保持秩序的最佳途徑。

回答

1

TPL數據流塊在輸出時保留輸入的順序。它們不會在塊內部保留執行順序,這就是爲什麼你看不到任何順序。

你可能想要做的就是用TransformBlock替換ActionBlock,以便實際上同時完成這項工作,並將它與一次更新UI的ActionBlock鏈接起來。

您也可以在UI線程上這一塊來看,這樣你就不需要使用Invoke

var transformBlock = new TransformBlock<int, int>(
    v => InsertIntoDatabaseAsync(v), 
    execOptions); 

var actionBlock = new ActionBlock<int>(x => 
{ 
    if (x) 
     txtOutput.Text += "Value Inserted for: " + v + Environment.NewLine; 
    else 
     txtOutput.Text += "Value Failed for: " + v + Environment.NewLine; 
}, new ExecutionDataflowBlockOptions { TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext() }) 

transformBlock.LinkTo(ActionBlock, new DataflowLinkOptions { PropagateCompletion = true }); 
+0

大。謝謝你回答並建議處理這種情況的方法。因爲我對此很陌生,所以只是好奇,TransformBlock比ActionBlock有什麼好處。 (我認爲這兩個參數的意思是一個用於輸入,另一個用於輸出,對嗎?我還需要等待TransformBlock或ActionBlock的完成嗎?最重要的是,如果任何一個塊發生異常,並且如果我拋出那麼所有的塊都會被異常終止? –

+0

好處是將工作分成兩塊,並且由於你需要鏈接它們,你首先需要成爲'TrasnformBlock'。 – i3arnon

+0

是的,第一種類型是輸入,第二種是輸入是輸出 – i3arnon