C#Windows服务嵌套了Parallel.ForEach

时间:2019-03-20 07:58:53

标签: c# parallel-processing windows-services parallel.foreach

我开发了一个Windows服务,该服务每5秒查询70个数据库。对于数据库中的每个条目,都将提取数据并将其写入文件系统上的.xml文件。该服务的目标是及时处理每个数据库的数据。时不时地在队列表中一次创建9000多个条目。现在仅激活了6个数据库。

共有两个部分: 数据库中有一个foreach循环,每个数据库中有一个用于排队表的循环。另外,已处理的条目数限制为200个,以便数据库彼此依次处理。

下面的代码片段显示了Windows服务的OnStart()方法。

protected override void OnStart(string[] args)
{
    mainTask = new Task(PollQueue, cts.Token, TaskCreationOptions.None);
    mainTask.Start();
}

由于时间敏感的原因,呼叫应并行化。遍历数据库的外部循环已更改为Parallel.ForEach()。这对于已经处于活动状态的6个数据库来说效果很好。之后,在队列表上迭代的内部循环也更改为Parallel.ForEach()。

只要只有一个数据库处于活动状态,这也可以工作。但是,一旦激活另一个,处理便停滞不前。使用MaxDegreeOfParallelism限制线程数没有帮助。没有这个限制,线程数大约为200。但是只处理了一个数据库。

这是一个代码示例。方法HandleQueueForEachLoop显示了第一个实现,而方法HandleQueueForEachParallel使用Parallel.ForEach进行了测试。此示例不可执行,但显示了实际的实现。

using Oracle.ManagedDataAccess.Client;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp8
{
    class Program
    {
        static void Main(string[] args)
        {
            HandleQueueForEachLoop();
            HandleQueueForEachParallel();
        }

        public static void HandleQueueForEachLoop()
        {
            NameValueCollection branches = (NameValueCollection)ConfigurationManager.GetSection("Branches");
            foreach (var branch in branches.AllKeys)
            {
                using (OracleConnection conn = new OracleConnection(branch))
                {
                    conn.Open();
                    var queue = ReadQueue(QueueEntityDescriptor, conn, MaximumRows);
                    if (queue.Count > 0)
                    {
                        List< DefaultEntity> entitiesWithData = FetchQueueData(queue, conn);
                        foreach (var entity in entitiesWithData)
                        {
                            if (entity.Records.Count == 0)
                            {
                                UpdateQueue(entity.EntityQueueId, QueueEntityDescriptor, conn, EntityQueueErrorEnum.NoRecordsInSelect);
                            }
                            else
                            {
                                try
                                {
                                    List<XElement> entityXml = CreateXmlForEntity(entity);
                                    var path = XmlPath;
                                    if (XMLPathSubFolder)
                                    {
                                        path = path + entity.EntityName + "\\";
                                    }

                                    foreach (var item in entityXml)
                                    {
                                        WriteDataXml(item, path, ".work");
                                    }

                                    UpdateQueue(entity.EntityQueueId, EntityQueueErrorEnum.Handled);
                                }
                                catch (Exception ex)
                                {
                                    UpdateQueue(entity.EntityQueueId, EntityQueueErrorEnum.Exception);
                                }
                            }

                        }
                    }
                }
            }
        }
        public static void HandleQueueForEachParallel()
        {
            NameValueCollection branches = (NameValueCollection)ConfigurationManager.GetSection("Branches");
            Parallel.ForEach(branches.AllKeys, branch =>
            {
                using (OracleConnection conn = new OracleConnection(branch))
                {
                    conn.Open();
                    var queue = ReadQueue(QueueEntityDescriptor, conn, MaximumRows);
                    if (queue.Count > 0)
                    {
                        Parallel.ForEach(queue,
                        new ParallelOptions { MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.75) * 2.0)) },
                        queueItem =>
                        {
                            DefaultEntity entity = FetchQueueEntryData(queueItem, conn);
                            if (entity.Records.Count == 0)
                            {
                                UpdateQueue(entity.EntityQueueId, EntityQueueErrorEnum.NoRecordsInSelect);
                            }
                            else
                            {
                                try
                                {
                                    var entityXml = CreateXmlForEntity(entity);
                                    var path = XmlPath;
                                    if (XMLPathSubFolder)
                                    {
                                        path = path + entity.EntityName + "\\";
                                    }

                                    foreach (var item in entityXml)
                                    {
                                        WriteData(item, path, ".work");
                                    }

                                    UpdateQueue(entity.EntityQueueId, EntityQueueErrorEnum.Handled);
                                }
                                catch (Exception ex)
                                {
                                    UpdateQueue(entity.EntityQueueId, EntityQueueErrorEnum.Exception);
                                }
                            }
                        });
                    }
                }
            });
        }
    }
}

该服务器具有4个CPU内核,这些内核很少使用。我们仅使用NuGet包OracleManageDataAccess为每个数据库创建一个连接。 .xml文件是使用XElement.Save()编写的。

我已经阅读了很多有关Parallel.ForEach()的内容,并搜索了一些教程。

我的问题:

  1. 使用嵌套的Parallel.ForEach循环明智吗?
  2. 是否还有其他最佳实践来并行处理多个数据库?

谢谢

0 个答案:

没有答案