right_there
right_there

Reputation: 175

Parallel.For w Action that creates different tasks, c#

I want to in parallel load a list with tasks, and then in parallel execute those tasks. The tasks take a method as action, but I need that method to work on different objects for every task. But I couldn't pass in parameters.

So I'm thinking of a solution like this:

public static Order order = new Order();
public static Queue<SubOrder> subsQ = order.SubOrders;

public void Work()
{
    Task[] taskList = TaskMaker(order.SubOrders.Count);
    Task.WaitAll(taskList);
}

public Task[] TaskMaker(int orderCount)
{
    Task[] taskList = new Task[order.SubOrders.Count];
    Parallel.For(0, taskList.Length, i => taskList[i] = new Task(ExecuteSubOrder) );
    return taskList;
}

public void ExecuteSubOrder()
{
    SubOrder subO = subsQ.Dequeue();
    // ... execute suborder, some webrequest etc.
    ordersCompletedTable.Execute(TableOperation.Insert(new SubOrder(subO.SubOrderId, subO.Status)),
        new TableRequestOptions() { RetryPolicy = new LinearRetry(TimeSpan.FromSeconds(3), 3) });
}

public class Order
{
    public Queue<SubOrder> SubOrders { get; set; }
}

The method executes an order, and upon every completed order, records that in an azure table. The method dequeues a new order for every task that I put into a Task[] with Parallel.For, so that I can then call Parallel.WaitAll with that task-array.

Can this be done so, or should I do it differently? Am I getting the job done in parallel here?

Upvotes: 2

Views: 286

Answers (1)

Douglas
Douglas

Reputation: 54887

You're using Parallel.For to create a new task for each suborder, but then retrieving the suborders from a shared data structure within the tasks. This is not the ideal route, since it will lead to a race hazard (if unsynchronized) or a bottleneck (if synchronized) when each task starts executing.

Instead, alter the definition of the ExecuteSubOrder method to accept its suborder as parameter, then use Parallel.ForEach to parallelize the suborders onto tasks. Parallel.ForEach also takes care of waiting for all suborders to complete before returning.

public void Work()
{
    Parallel.ForEach(order.SubOrders, ExecuteSubOrder);
}

public void ExecuteSubOrder(SubOrder subO)
{
    // ... execute suborder, some webrequest etc.
    ordersCompletedTable.Execute(TableOperation.Insert(new SubOrder(subO.SubOrderId, subO.Status)),
        new TableRequestOptions() { RetryPolicy = new LinearRetry(TimeSpan.FromSeconds(3), 3) });
}

Upvotes: 2

Related Questions