发生超时时,网络绑定I / O的并行缓慢

时间:2016-02-28 22:52:27

标签: c# plinq

我正在并行化一种高度依赖WinAPI NetAPI32调用的方法。如果用户输入已关闭的主机或数百个列表中的多个主机,则呼叫有时会超时。

int prevThreads, prevPorts;
ThreadPool.GetMinThreads(out prevThreads, out prevPorts);
ThreadPool.SetMinThreads(20, prevPorts);

var parallelScanList = computersToScan.AsParallel().WithExecutionMode(ParallelExecutionMode.ForceParallelism).WithDegreeOfParallelism(20);

Api.WinApi.AdvApi.LogonAndImpersonate(connection.UserCredential);

foreach (var computer in parallelScanList)
{
        //...
        //this takes a long time to timeout
        status = NetApi.NetUserEnum(computer.DnsHostname, 2,
                (int)NetApi.NetUserEnumFilter.FILTER_NORMAL_ACCOUNT,
                out userbufPtr, (int)LmCons.MAX_PREFERRED_LENGTH, out userEntriesRead, out totalEntries,
                out userResumeHandle);

}

我们在使用消费者/生产者的C客户端中具有类似的逻辑。旋转20个线程并让它们读取列表直到它耗尽。

function StartProcessingHosts()
{
  for 1 to 20
     StartProcessThread()
}

function ProcessHostsThread()
{
  while(moreHosts)
  {
     //obviously synchronization around here
     var host = popHost();
     DoSomething(host);
  }
}

这非常快,因为这些网络电话的所有等待以及无法连接到被击倒的主机的可能性。

我目前在C#中这样做的方式似乎是一次一个。

2 个答案:

答案 0 :(得分:1)

<强>更新

我知道,问题是foreach循环。您可能认为通过创建查询AsParallel然后在foreach中执行它将使其并行。当然这不会发生。此外,使用PLINQ可以实现与svick's answer中所示的相同。

但是,这是另一种并行化代码的方法,我在下面提到它,因为svick的答案也因为设置MaxDegreeOfParallelism = 20而不保证20次并行执行而受到影响。它仍然只是并行执行的上限,而不是下限。如果PLINQ执行引擎认为它应该只启动5个并行执行,它将仅启动5,这是完全合法的执行。

以下代码保证了20个并行执行:

var concurrentScanList = new ConcurrentQueue<Computer>(computersToScan);
var taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None);
var taskArray = new Task[20];

//Initializing the tasks
for (var index = 0; index < taskArray.Length; index++)
{
    taskArray[index] = taskFactory.StartNew(() =>
    {
        Computer host;
        while (concurrentScanList.TryDequeue(out host))
        {
            DoSomething(host);
        }
    });
}

//Wait for all tasks to finish - queue will be empty then
Task.WaitAll(baseProcessorTaskArray);

旧答案:

WithDegreeOfParallelism()是,

  

将用于处理查询的最大并发执行任务数。

...我认为,由于并发执行的最小任务数量不固定,因此可能为1。

基本上,你的猜测可能是正确的,这种执行不会并行发生,因此超时。此外,即使 并行发生并行度等于20,也不能保证这样。

我的建议是你将“计算机扫描”放在BlockingCollection中,然后产生20个从这个BlockingCollection读取计算机的任务,然后扫描它。这种实现自然会是 Producer Consumer ,因为这是问题设计的内在质量。

答案 1 :(得分:0)

PLINQ是Parallel LINQ的缩写,您猜对了,并行化LINQ查询。例如,如果您编写collection.AsParallel().Where(/* some condition */).Select(/* some projection */).ToList(),则Where()Select()将并行执行。

但是你不这样做,你打电话给AsParallel(),说&#34;以下LINQ查询应该并行执行&#34;。然后,通过调用WithExecutionMode()WithDegreeOfParallelism()来配置即将出现的查询的并行性。然后你实际上没有任何LINQ查询,而是使用foreach,它将连续迭代集合。

如果你想并行执行foreach,你不想要PLINQ,你想要Parallel.ForEach()

Parallel.ForEach(computersToScan, new ParallelOptions { MaxDegreeOfParallelism = 20 },
    computer =>
    {
        //...
    });