2012-08-01 47 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