多线程多迭代

时间:2019-06-07 10:23:34

标签: c# multithreading concurrency

我重写了最初只用于一个线程的方法,使其可用于多个线程。现在,此方法接受两个并发的集合:ConcurrentBag,它是List;和ConcurrentQueue,它是Queue

目的是匹配两个集合中的两个标题并做出一些逻辑,这是在ConcurrentBag项目中进行简单的值分配。我肯定知道ConcurrentBag中的所有符号都在ConcurrentQueue中。

当我为多线程编写此代码时,发生了一些标题不匹配(〜20%)的情况,而在线程上时则没有发生。只有在调试期间,我才能匹配这些标题,然后分配值。迭代这两个集合必须有一些问题。也许在同一时间,许多线程从同一项目读取值,但仅读取应该不会有问题吗?

下面的代码:

public void UpdateWithPercent(ref ConcurrentBag<Book> refList, ConcurrentQueue<Book> list)
{
    var size = list.Count;
    int numProcs = Environment.ProcessorCount;
    var divider = CalculatBiggestDivider(size);
    var nextIteration = 0;
    var remainingWork = numProcs;
    var internalRefList = refList;
    using (ManualResetEvent mre = new ManualResetEvent(false))
    {
        for (int i = 0; i < numProcs; i++)
        {
            ThreadPool.QueueUserWorkItem(delegate
            {
                IEnumerable<Book> partialList;
                while (-(nextIteration - Interlocked.Add(ref nextIteration, (partialList = DequeueChunk(list, divider)).Count()))> 0)
                {
                    foreach (var item in partialList)
                    {
                        foreach (var x in internalRefList)
                        {
                            if (x.Title == item.Title)
                            {
                                x.Orders += item.Orders;
                                break;
                            }
                        };
                    }
                }

                if (Interlocked.Decrement(ref remainingWork) == 0)
                {
                    mre.Set();
                }
            });
        }

        mre.WaitOne();
    }

    refList = internalRefList;
}

private int CalculatBiggestDivider(int count)
{
    var divider = 1;
    for (int i = 30; i > 0; i--)
    {
        if (count % i == 0)
        {
            divider = i;
            break;
        }
    }

    return divider;
}

private IEnumerable<T> DequeueChunk<T>(ConcurrentQueue<T> queue, int chunkSize)
{
    for (int i = 0; i < chunkSize && queue.Count > 0; i++)
    {
        T item;
        bool success = queue.TryDequeue(out item);
        if (!success)
        {
            i = chunkSize;
            continue;
        }
        yield return item;
    }
}

1 个答案:

答案 0 :(得分:0)

最终,我决定退出nestedloops,并使用ConcurrentDictionary。现在可以了。

public void UpdateWithPercent(ref ConcurrentDictionary<string, Book> refList, List<Book> list, int ticker, int maxTimes)
{
    var size = list.Count;
    int numProcs = Environment.ProcessorCount;
    var divider = CalculatBiggestDivider(size);
    var nextIteration = 0;
    var remainingWork = numProcs;
    var internalRefList = refList;
    using (ManualResetEvent mre = new ManualResetEvent(false))
    {
        for (int i = 0; i < numProcs; i++)
        {
            ThreadPool.QueueUserWorkItem(delegate
            {
                int index = 0;
                while ((index = Interlocked.Add(ref nextIteration, divider) - divider) < size)
                {
                    foreach (var item in list.GetRange(index, divider))
                    {
                        Book x;
                        if (internalRefList.TryGetValue(item.Title, out x))
                        {
                                x.Orders += item.Orders;
                        }
                    };
                }

                if (Interlocked.Decrement(ref remainingWork) == 0)
                {
                    mre.Set();
                }
            });
        }

        mre.WaitOne();
    }

    refList = internalRefList;
}