Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Parallel.ForEach具有自定义TaskScheduler以防止OutOfMemoryException_C#_Multithreading_Parallel Processing - Fatal编程技术网

C# Parallel.ForEach具有自定义TaskScheduler以防止OutOfMemoryException

C# Parallel.ForEach具有自定义TaskScheduler以防止OutOfMemoryException,c#,multithreading,parallel-processing,C#,Multithreading,Parallel Processing,我通过Parallel.ForEach处理大小千差万别的PDF文件(简单的2MB到几百MB的高DPI扫描),偶尔会遇到OutOfMemoryException,这是可以理解的,因为该进程是32位的,Parallel.ForEach产生的线程占用了未知数量的内存消耗工作 限制MaxDegreeOfParallelism确实有效,但当有大量(10k+)小PDF可供使用时,吞吐量不足,因为由于所述线程的内存占用较小,可能会有更多线程在工作。这是一个CPU密集的并行进程。ForEach在偶尔遇到一组大型

我通过Parallel.ForEach处理大小千差万别的PDF文件(简单的2MB到几百MB的高DPI扫描),偶尔会遇到OutOfMemoryException,这是可以理解的,因为该进程是32位的,Parallel.ForEach产生的线程占用了未知数量的内存消耗工作

限制
MaxDegreeOfParallelism
确实有效,但当有大量(10k+)小PDF可供使用时,吞吐量不足,因为由于所述线程的内存占用较小,可能会有更多线程在工作。这是一个CPU密集的并行进程。ForEach在偶尔遇到一组大型PDF并出现OutOfMemoryException之前,很容易达到100%的CPU。运行性能分析器可以对此进行备份

根据我的理解,为Parallel.ForEach使用分区器不会提高我的性能

这导致我使用传递给my Parallel.ForEach的自定义
TaskScheduler
MemoryFailPoint
检查。在它周围搜索,似乎很少有关于创建自定义
TaskScheduler
对象的信息

在Stackoverflow的各种答案之间,我创建了自己的
TaskScheduler
,并拥有自己的
QueueTask
方法:

protected override void QueueTask(Task task)
{
    lock (tasks) tasks.AddLast(task);
    try
    {
        using (MemoryFailPoint memFailPoint = new MemoryFailPoint(600))
        {
            if (runningOrQueuedCount < maxDegreeOfParallelism)
            {
                runningOrQueuedCount++;
                RunTasks();
            }
        }
    }
    catch (InsufficientMemoryException e)
    {     
        // somehow return thread to pool?           
        Console.WriteLine("InsufficientMemoryException");
    }
}
受保护的覆盖无效队列任务(任务任务)
{
锁定(任务)任务。添加最后一个(任务);
尝试
{
使用(MemoryFailPoint memFailPoint=新的MemoryFailPoint(600))
{
if(runningOrQueuedCount
虽然try/catch有点昂贵,但我的目标是在可能的最大大小PDF(+一点额外的内存开销)为600MB时捕获抛出OutOfMemoryException。当我发现内存不足异常时,这个解决方案似乎会扼杀试图完成这项工作的线程。有了足够大的PDF,我的代码就变成了一个单线程Parallel.ForEach

在Parallel.ForEach和OutOfMemoryExceptions上的Stackoverflow上发现的其他问题似乎不适合我在线程上动态内存使用情况下实现最大吞吐量的用例,通常只是利用
MaxDegreeOfParallelism
作为静态解决方案,例如:

因此,要获得可变工作内存大小的最大吞吐量,可以:

  • 当线程通过
    MemoryFailPoint
    检查被拒绝工作时,如何将其返回到线程池中
  • 当有空闲内存时,如何/在何处安全地生成新线程以重新开始工作
编辑:
磁盘上的PDF大小可能不会线性表示内存中的大小,因为光栅化和光栅化图像处理组件依赖于PDF内容

使用来自的
limitedconcurrencylevelstaskscheduler
,我能够进行一个小的调整,以获得我想要的东西。以下是修改后的
LimitedConcurrencyLevel TaskScheduler
类的
NotifyThreadPoolOfPendingWork
方法:

private void NotifyThreadPoolOfPendingWork()
{
    ThreadPool.UnsafeQueueUserWorkItem(_ =>
    {
        // Note that the current thread is now processing work items.
        // This is necessary to enable inlining of tasks into this thread.
        _currentThreadIsProcessingItems = true;
        try
        {
            // Process all available items in the queue.
            while (true)
            {
                Task item;
                lock (_tasks)
                {
                    // When there are no more items to be processed,
                    // note that we're done processing, and get out.
                    if (_tasks.Count == 0)
                    {
                        --_delegatesQueuedOrRunning;
                        break;
                    }

                    // Get the next item from the queue
                    item = _tasks.First.Value;
                    _tasks.RemoveFirst();
                }

                // Execute the task we pulled out of the queue
                //base.TryExecuteTask(item);

                try
                {
                    using (MemoryFailPoint memFailPoint = new MemoryFailPoint(650))
                    {
                        base.TryExecuteTask(item);
                    }
                }
                catch (InsufficientMemoryException e)
                {
                    Thread.Sleep(500);

                    lock (_tasks)
                    {
                        _tasks.AddLast(item);
                    }
                }

            }
        }
        // We're done processing items on the current thread
        finally { _currentThreadIsProcessingItems = false; }
    }, null);
}
我们来看看渔获量,但反过来看。我们将要处理的任务添加回任务列表(
\u tasks
),这将触发一个事件,以获取一个可用线程来处理该任务。但是我们首先休眠当前线程,以便它不会直接执行任务,并返回失败的
内存故障点检查