如何在C#中使用一次一个元素BlockingCollection <t>?</t>

时间:2012-10-21 00:06:17

标签: c# .net arrays task-parallel-library blockingcollection

我已经看过很多关于如何在生产者 - 消费者场景中使用BlockingCollection<T>的例子,甚至是如何一次消费一个元素here。我对并行编程很新,所以我面临以下问题:

问题确实是如何在下面的示例中编写方法ConsumerProducerExample.ConsumerMethod,以便它为数组中的每个元素消耗double的前2 BlockingCollection<Double>,然后继续为数组中的每个元素使用下一个double,依此类推。

我在下面的示例中编写了该方法,但我想要它的工作方式。基于我上面链接的示例,我知道该怎么做。它正好相反:就像在这里一样,它会在跳转到数组的下一个double之前消耗每个BlockingCollection<Double>。而且,再次,我希望它只消耗两个double,然后跳转到下一个BlockingCollection<Double>,消耗两个double等。完成数组循环后,它将继续消耗每个double元素的下两个BlockingCollection<Double>,依此类推。

public class ConsumerProducerExample
{
    public static BlockingCollection<Double>[] data;
    public static Int32 nEl = 100;
    public static Int32 tTotal = 1000;
    public static Int32 peakCounter = 0;

    public static void InitData()
    {
        data = new BlockingCollection<Double>[nEl];
        for (int i = 0; i < nEl; i++) data[i] = new BlockingCollection<Double>();
    }

    public static void ProducerMethod()
    {
        Int32 t = 0, j;
        while (t < ConsumerProducerExample.tTotal)
        {
            j = 0;
            while (j < ConsumerProducerExample.nEl)
            {
                data[j].Add(Math.Sin((Double)t / 10.0D));
                j++;
            }
            t++;
        }
        j = 0;
        while (j < ConsumerProducerExample.nEl)
        {
            data[j].CompleteAdding();
            j++;
        }
    }

    public static void ConsumerMethod()
    {
        // THE PROBLEM IS WITH THIS METHOD

        Double x1, x2;
        Int32 j = 0;
        while (j < Program.nEl)
        {
            while (!data[j].IsCompleted)
            {
                try
                {
                    x1 = data[j].Take();
                    x2 = data[j].Take();
                }
                catch (InvalidOperationException)
                {
                    break;
                }

                if (x1 * x2 < 0.0)
                {
                    Program.peakCounter++;
                }
            }

            j++;
        }
    }
}

他们应该像这样使用:

ConsumerProducerExample.InitData();
Task task1 = Task.Factory.StartNew(ConsumerProducerExample.ProducerMethod);
Task task2 = Task.Factory.StartNew(ConsumerProducerExample.ConsumerMethod);

有什么建议吗?

简而言之,这是尝试同时计算nEl微分方程解的峰值(在此示例中,解由sin(x)表示)。

1 个答案:

答案 0 :(得分:2)

基本上,您需要摆脱内部循环并添加另一个外部循环,迭代所有集合,直到它们完成。类似的东西:

while (!data.All(d => d.IsCompleted))
{
    foreach (var d in data)
    {
        // only takes elements from the collection if it has more than 2 elements
        if (d.Count >= 2)
        {
            double x1, x2;
            if (d.TryTake(out x1) && d.TryTake(out x2))
            {
                if (x1 * x2 < 0.0)
                    peakCounter++;
            }
            else
                throw new InvalidOperationException();
        }
    }
}