繁体   English   中英

线程池顺序运行?

[英]Thread pool running sequentially?

我正在编写一个函数来获取目录中的所有文件,但是通过将每个子目录添加到线程池来并行执行。 我认为这意味着每个目录都会被并行遍历,因为有很多子目录,所以它比按顺序执行要快得多。 我的代码如下:

    private object addlock = new object();
    private void addFiles(string[] newFiles)
    {
        lock (addlock) {
            files.AddRange( newFiles );
            Console.WriteLine( "Added {0} files", newFiles.Length );
        }
    }

    private void getFilesParallel(string dir)
    {
        if (!Directory.Exists( dir )) {
            return;
        }

        string[] dirs = Directory.GetDirectories( dir, "*", SearchOption.TopDirectoryOnly );
        ManualResetEvent mre = new ManualResetEvent( false );

        ThreadPool.QueueUserWorkItem( (object obj) =>
        {
            addFiles( Directory.GetFiles( dir, "*", SearchOption.TopDirectoryOnly ) );
            mre.Set();
        } );

        Process currentProcess = Process.GetCurrentProcess();
        long memorySize = currentProcess.PrivateMemorySize64;

        Console.WriteLine( "Used {0}", memorySize );

        foreach (string str in dirs) {
            getFilesParallel( str );
        }

        mre.WaitOne();
    }

问题是我得到这样的输出:

Added 34510 files
Used 301420544
Added 41051 files
Used 313937920
Added 39093 files
Used 322764800
Added 44426 files
Used 342536192
Added 30772 files
Used 350728192
Added 36262 files
Used 360329216
Added 31686 files
Used 368685056
Added 33194 files
Used 374894592
Added 34486 files
Used 384057344
Added 37298 files
Used 393998336

这表明我的代码是按顺序运行的,因为我希望在不同的线程上运行时,在clumps中找到每个语句。 我使用不同的文件夹运行了几次,结果总是一样的。 这为什么顺序运行?

您只有一个物理磁盘驱动器。 磁盘的磁头一次只能放在一个地方。 你要求它同时提供两条信息并不能让它实际上同时出现在两个地方。

程序中有一些少量的CPU绑定工作实际上可以并行化,但这不是主要的瓶颈。

如果您有多个物理磁盘驱动器和每个驱动器上的数据,那么您可以并行访问每个驱动器上的数据,并且实际上可以并行完成相关工作。

准确地对此进行基准测试有点困难,因为如果您有足够的内存,第一次运行将缓存数据,并且相同文件夹的后续枚举可以在不访问磁盘的情况下运行。

还值得考虑的是,如果你有一个SSD,它将从并行操作中获益更多,因为它支持更多的IOPS,因为它没有任何可移动的部件等待。

此代码显示,在针对SSD运行时,或者当数据已经缓存时,我的四核i5上的并行速度可以比单线程快2到3倍。

它演示了Parallel.ForEach的使用,它可以消除任务并行性带来的很多痛苦。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

namespace FilesReader
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = args[0];
            RunTrial(path, false);
            RunTrial(path, true);
        }

        private static void RunTrial(string path, bool useParallel)
        {
            Console.WriteLine("Parallel: {0}", useParallel);

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            FileListing listing = new FileListing(path, useParallel);
            stopwatch.Stop();

            Console.WriteLine("Added {0} files in {1} ms ({2} file/second)", 
                listing.Files.Count, stopwatch.ElapsedMilliseconds, 
                (listing.Files.Count * 1000 / stopwatch.ElapsedMilliseconds));
        }
    }

    class FileListing
    {
        private ConcurrentList<string> _files;
        private bool _parallelExecution;

        public FileListing(string path, bool parallelExecution)
        {
            _parallelExecution = parallelExecution;
            _files = new ConcurrentList<string>();
            BuildListing(path);
        }

        public ConcurrentList<string> Files
        {
            get { return _files; }
        }

        private void BuildListing(string path)
        {
            string[] dirs = null;
            string[] files = null;
            bool success = false;

            try
            {
                dirs = Directory.GetDirectories(path, "*", SearchOption.TopDirectoryOnly);
                files = Directory.GetFiles(path);
                success = true;
            }
            catch (SystemException) { /* Suppress security exceptions etc*/ }

            if (success)
            {
                Files.AddRange(files);

                if (_parallelExecution)
                {
                    Parallel.ForEach(dirs, d => BuildListing(d));
                }
                else
                {
                    foreach (string dir in dirs)
                    {
                        BuildListing(dir);
                    }
                }
            }
        }
    }

    class ConcurrentList<T>
    {
        object lockObject = new object();
        List<T> list;

        public ConcurrentList()
        {
            list = new List<T>();
        }

        public void Add(T item)
        {
            lock (lockObject) list.Add(item);

        }
        public void AddRange(IEnumerable<T> collection)
        {
            lock (lockObject) list.AddRange(collection);
        }

        public long Count
        {
            get { lock (lockObject) return list.Count; }
        }
    }
}

我考虑使用Concurrent集合而不是滚动线程安全列表实现,但事实证明它们慢了约5%。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM