2012-08-01 132 views
1

所有,我有一個方法叫做TaskSpin,它使用TPL在後臺線程上運行傳遞的方法。使用任務繼續堆棧作業

public TaskSpin(Func asyncMethod, object[] methodParameters) 
{ 
    ... 
    asyncTask = Task.Factory.StartNew<bool>(() => 
     asyncMethod(uiScheduler, methodParameters)); 

    asyncTask.ContinueWith(task => 
    { 
     ... 
     // Finish the processing update UI etc. 
     ... 
     if (isStacked && task.Status == TaskStatus.RanToCompletion) 
      ProcessNextTask(dataGridViewSite); 
    } 
    ... 
} 

這個程序是公認的,一時間火過一個方法,但我最近一直需要排隊多種方法和順序運行它們。要做到這一點(由this answer資助)我寫了下面的按鈕單擊事件處理程序

private void buttonProcAllUrg_Click(object sender, EventArgs e) 
{ 
    // Build the stacker. 
    Dictionary<Func<TaskScheduler, object[], bool>, object[]> taskPair = 
     new Dictionary<Func<TaskScheduler, object[], bool>, object[]>(); 
    this.taskQueue = 
     new Queue<KeyValuePair<Func<TaskScheduler, object[], bool>, object[]>>(); 

    // Populate the stacker. 
    foreach (DataGridViewRow row in this.DataGridViewDrg.Rows) 
    { 
     KeyValuePair<Func<TaskScheduler, object[], bool>, object[]> task = 
      new KeyValuePair<Func<TaskScheduler, object[], bool>, object[]> 
      (BuildDrgDataForSite, DrgDataRowInfo(row.Index));  
     this.taskQueue.Enqueue(task); 
    } 

    // Launch the queue. 
    ProcessNextTask(this.DataGridViewDrg); 
} 

和ProcessNextTask被定義爲

private void ProcessNextTask(DataGridView dataGridView) 
{  
    try  
    { 
     // Queue empty. 
     bIsStacked = true; 
     if (this.taskQueue.Count <= 0) 
     { 
      bIsStacked = false; 
      return; 
     } 

     // Launch new task. 
     KeyValuePair<Func<TaskScheduler, object[], bool>, object[]> item = this.taskQueue.Dequeue(); 
     Task<bool> asyncBuildDataTask = null; 
     TaskSpin(asyncBuildDataTask, uiScheduler, mainForm, 
        item.Key, item.Value, dataGridView, 
        "Process stack successfully executed", true, bIsStacked); 
    }  
    catch(InvalidOperationException)  
    { 
     // Nothing left in stack. 
     bIsStacked = false;  
    } 
} 

這工作得很好的方法,但第一個任務已經運行後在繼續進行第二次(或更多次)調用ProcessNextTask時,GUI變得無響應。我能做些什麼來確保UI線程在第二次調用時不被阻塞?

注意。我曾嘗試推出ProcessNextTask方法在另一個線程使用UI線程同步上下文

Task task = Task.Factory.StartNew(() => 
{ 
    ProcessNextTask(dataGridView); 
}, CancellationToken.None, 
    TaskCreationOptions.LongRunning, 
    uiScheduler); 

其中TaskScheduler uiSchduler = TaskScheduler.FromCurrentSynchonisationContex();

編輯:我已經attampted創建BlockingCollection,以方便我想基於以下@Reed科普塞的回答做的,但我從來沒有這樣做,將在這裏歡迎任何建議

BlockingCollection<Action<object[]>> taskQueue = 
    new BlockingCollection<Action<object[]>>(); 
foreach (DataGridViewRow row in this.DataGridViewDrg.Rows) 
{ 
    Action<object[]> tmpAction = del => 
     {AsyncBuildDrgDataForSite(DrgDataRowInfo(row.Index)); }; 
    taskQueue.Add(tmpAction); 
} 

感謝您的時間。

+0

我爲什麼要解僱史蒂夫喬布斯? – MoonKnight 2012-08-01 16:26:27

+0

我喜歡它......:] – MoonKnight 2012-08-01 17:03:13

回答

2

我會建議考慮這個不同。

如果您嘗試「堆疊作業」,通常更好的做法是將例程修改爲生產者/使用者場景。 BlockingCollection<T>爲此提供了適當的框架,因爲它提供了一個線程安全隊列,您可以使用它來添加工作項目,然後創建一個任務來處理它們(通過GetConsumingEnumerable())。項目完成後,您可以更新您的用戶界面,然後在添加新項目時重新啓動「製作人」任務等。

+0

我已經編輯了一個新的嘗試,你有什麼建議。你可以看看嗎?這將非常感激。感謝您的時間... – MoonKnight 2012-08-01 16:21:23

+0

@Killercam您可能會遇到關閉問題 - 您必須在您的foreach中暫時使用。參見:http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx – 2012-08-01 16:28:52