我开发了一个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()的内容,并搜索了一些教程。
我的问题:
谢谢