views:

46

answers:

1

I worked on an example in the text book about parallel programming in C#. The book suggests Parallel.Invoke can replace the creation, invocation and waiting of tasks. However, I tried and found that if I use Parallel.Invoke, the tasks wouldn't finish before returning a value. But in theory, Parallel.Invoke should always wait.

The code:

    private byte[] getDataForGraph(int dataSize)
    {
        byte[] data = new byte[dataSize];


        Parallel.Invoke(
            () => Task.Factory.StartNew(() => generateGraphData(data, 0, pixelWidth / 8)),
            () => Task.Factory.StartNew(() => generateGraphData(data, pixelWidth / 8,
                pixelWidth / 4)),
            () => Task.Factory.StartNew(() => generateGraphData(data, pixelWidth / 4,
                pixelWidth * 3 / 8)),
            () => Task.Factory.StartNew(() => generateGraphData(data, pixelWidth * 3 / 8,
                pixelWidth / 2)));

        return data;
    }

And the way to execute the function:

Task<byte[]> getDataTask = Task<byte[]>.Factory.StartNew(() => getDataForGraph(dataSize));
byte[] data = getDataTask.Result;

The private void generateGraphData(byte[] data, int partitionStart, int partitionEnd) is a function that fills the data array, from partitionStart to partitionEnd.

If I run the program, only part of the array is filled. But if I replace the Invoke with

Task first = Task.Factory.StartNew(() => generateGraphData(data, 0, pixelWidth / 8));
Task second = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth / 8, pixelWidth / 4));
Task third = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth / 4, pixelWidth * 3 / 8));
Task fourth = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth * 3 / 8, pixelWidth / 2));
Task.WaitAll(first, second, third, fourth);

The program runs as expected (the array is fully filled).

What may be the problem here?

Thanks in advance.

+3  A: 

It performs the actions until they are complete. In your case, each action is to call Task.Factory.StartNew(...) - and that has completed; however, there is no guarantee that each of thses tasks has been queued/processed yet. The difference is the WaitAll (which you haven't called in your Parallel example).

One option here would be to reduce this to:

Parallel.Invoke(
    () => generateGraphData(data, 0, pixelWidth / 8),
    () => generateGraphData(data, pixelWidth / 8, pixelWidth / 4),
    () => generateGraphData(data, pixelWidth / 4, pixelWidth * 3 / 8),
    () => generateGraphData(data, pixelWidth * 3 / 8, pixelWidth / 2)
);
Marc Gravell
Thanks, that explains a lot. I just copied the code, and didn't check it carefully.
LLS