Question

I have a method that invokes a passed Func on a background thread called TaskSpin. This does one method at a time based on the site clicked in a DataGridView (DGV), and it can SHOULD ONLY do one at a time. So I click on a site (a button cell in the DGV) and it run the method on that site. Now I have many sites and do not want to click and wait hundreds of times, instead I want to batch process all the sites contained in the DGV. I cannot use a straight loop to run these, i.e.

foreach (DataGridViewRow row in this.DGV)
    TaskSpin(SomeMethod, SomeParameterArray);

as this returns to caller almost immediately, and will begin running the following site which is not good as they use shared files. So I need a way to queue the sites up to be execute one-by-one. I have decided to use a ConncurrentCollection - BlockingCollection, but I am stuck. In some button click event I have

BlockingCollection<Action<object[]>> taskQueue = 
    new BlockingCollection<Action<object[]>>();
List<object[]> paramQueue = new List<object[]>();
foreach (DataGridViewRow row in this.DataGridViewDrg.Rows) 
{
    paramQueue.Add(DrgDataRowInfo(row.Index));
    Action<object[]> action = new Action<object[]>(AsyncMethod);
    taskQueue.Add(action);
}
int i = 0;
foreach (Action<object[]> action in taskQueue.GetConsumingEnumerable())
    action(paramQueue[i]);

where AsyncMethod launched a set method on the background thread using TPL.

private void AsyncMethod(object[] siteInfo)
{
    TaskSpin(BuildDataForSite, siteInfo);
}

My expectation is that this would run each Action<object[]> one after the other. But this is just freezing my application. What am I doing wrong here?

Thanks for your time.

*Edit. TaskSpin*

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

    asyncTask.ContinueWith(task =>
    {
        ...
        // Finish the processing update UI etc.
        ...
    }
    ...
}
Was it helpful?

Solution

The code

foreach (Action<object[]> action in taskQueue.GetConsumingEnumerable())
    action(paramQueue[i]);

is running on your UI thread, thus causing the block.

Your requirement seems to be that you want to process a list of things, one at a time, on a non-UI thread. To accomplish that with your current code and minimal modification, you can move your foreach above into a separate thread, e.g. by creating a single Task that runs the foreach.

Task.Factory.StartNew(() =>
{
    foreach (Action<object[]> action in taskQueue.GetConsumingEnumerable())
        action(paramQueue[i]);
});

OTHER TIPS

Although I am late

My solution is to use Task.Run instead of Task.Factory.StartNew

Task.Run(() =>
{
    foreach (Action<object[]> action in taskQueue.GetConsumingEnumerable())
        action(paramQueue[i]);
});

I am posting this answer based on this article

I also was using Task.Factory.StartNew but then it sometimes ran on my UI thread causing my app to hang. Task.Run solved my issue.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top