5

C# 爽点记录-2:并行任务

 1 year ago
source link: https://scottyeung.top/2022/csharp-merit-2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

C# 爽点记录-2:并行任务

 2022.11.25 2022.11.25  Posts  

之前有个小需求,想写个小工具来统计电脑上各个文件夹和文件的大小,方便自己找出占用空间多的文件夹进行清理,释放硬盘空间。代码实现起来很简单,利用各种编程语言提供的读取文件系统当中的文件和大小接口,然后做个累加统计就可。

具体的思路是:

  1. 读取目录下的所有文件和文件夹
  2. 文件直接获取大小
  3. 文件夹则递归调用计算函数,获取大小
  4. 最后把所有文件和文件夹的大小累加起来

一个函数就能搞定,但是如果当目录下的文件夹数量太多,或者是目录的层级太深,跑起来就会很慢,尤其是通常来说硬盘里面的文件和文件夹数量这么多,随随便便就能跑个几十分钟(还是非磁盘的根目录)。

于是想当然地就想着用多线程来并行计算加速。因为最开始的版本是用 Go 实现,所以就把每次递归调用变成了开一个协程来跑。一开始觉得可能会用太多的协程,占用过多资源,所以还写了个简单的协程池,然而这样限制协程数量的话,导致不够协程来计算新的文件夹,就会出现死锁的情况。所以就还是每个调用开个协程来跑,不过速度还是比较感人。

然后昨晚看到一个 C# 的写法,一个函数就能够就能够简单开启并行任务,也不用像在 Go 里面用各种锁或者 channel 来进行数据同步。

public static long GetDirectorySize(this System.IO.DirectoryInfo directoryInfo, bool recursive = true)
{
    var startDirectorySize = default(long);
    if (directoryInfo == null || !directoryInfo.Exists)
        return startDirectorySize; //Return 0 while Directory does not exist.

    //Add size of files in the Current Directory to main size.
    foreach (var fileInfo in directoryInfo.GetFiles())
        System.Threading.Interlocked.Add(ref startDirectorySize, fileInfo.Length);

    if (recursive) //Loop on Sub Direcotries in the Current Directory and Calculate it's files size.
        System.Threading.Tasks.Parallel.ForEach(directoryInfo.GetDirectories(), (subDirectory) =>
    System.Threading.Interlocked.Add(ref startDirectorySize, GetDirectorySize(subDirectory, recursive)));

    return startDirectorySize;  //Return full Size of this Directory.
}

C# 可以直接使用 System.Threading.Tasks 来并行跑任务,然后通过 System.Threading.Interlocked 直接原子式修改变量,能够直接在几十秒内扫描完 380+g 的整个盘,相比于需要差不多十分钟的Go 版本,速度简直惊人,加上 dotnet 还能直接打包成 exe,用起来体验简直起飞。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK