【问题标题】:What is the best way to work with multiple files in multithread in C#?在 C# 中的多线程中处理多个文件的最佳方法是什么?
【发布时间】:2015-05-27 23:03:11
【问题描述】:

我正在创建一个 Windows 窗体应用程序,我在其中选择了一个包含多个 *.txt 文件的文件夹。它们的长度可能从几千行 (kB) 到多达 5000 万行 (1GB) 不等。代码的每一行都包含三个信息。 long 中的日期,int 中的位置 id 和 float 中的值都用分号 (;) 分隔。我需要计算所有这些文件中的最小值和最大值,并告诉它在哪个文件中,然后是最常见的值。

我已经验证了这些文件并将其存储在数组列表中。我正在打开一个线程来逐个读取文件,然后逐行读取数据。它工作正常,但是当有 1GB 文件时,我的内存不足。我试图将值存储在字典中,其中键是日期,值是一个对象,其中包含从该行加载的所有信息以及文件名。我看到我不能使用字典,因为在大约 6M 值时,我的内存不足。所以我可能应该用多线程来做。我虽然可以运行两个线程,一个读取文件并将信息放入某种容器中,另一个从中读取并进行计算,然后从容器中删除值。但我不知道哪个容器可以做这样的事情。此外,我需要计算最常见的值,因此需要将它们存储在某个地方,这会导致我回到某种字典,但我已经知道我会用完内存。我也没有太多线程经验,所以我不知道有什么可能。到目前为止,这是我的代码:

图形界面:

namespace STI {
    public partial class GUI : Form {
        private String path = null;
        public static ArrayList txtFiles;

        public GUI() {
            InitializeComponent();
            _GUI1 = this;
        }

       //I run it in thread. I thought I would run the second 
       //one here that would work with the values inputed in some container
        private void buttonRun_Click(object sender, EventArgs e) {
            ThreadDataProcessing processing = new ThreadDataProcessing();
            Thread t_process = new Thread(processing.runProcessing);
            t_process.Start();

            //ThreadDataCalculating calculating = new ThreadDataCalculating();
            //Thread t_calc = new Thread(calculating.runCalculation());
            //t_calc.Start();

        }


    }
}

ThreadProcessing.cs

namespace STI.thread_package {
    class ThreadDataProcessing {
        public static Dictionary<long, object> finalMap = new Dictionary<long, object>();

        public void runProcessing() {
            foreach (FileInfo file in GUI.txtFiles) {
                using (FileStream fs = File.Open(file.FullName.ToString(), FileMode.Open))
                using (BufferedStream bs = new BufferedStream(fs))
                using (StreamReader sr = new StreamReader(bs)) {
                    String line;
                    String[] splitted;
                    try { 
                        while ((line = sr.ReadLine()) != null) {
                            splitted = line.Split(';');

                            if (splitted.Length == 3) {
                                long date = long.Parse(splitted[0]);
                                int location = int.Parse(splitted[1]);
                                float value = float.Parse(splitted[2], CultureInfo.InvariantCulture);

                                Entry entry = new Entry(date, location, value, file.Name);

                                if (!finalMap.ContainsKey(entry.getDate())) {
                                    finalMap.Add(entry.getDate(), entry);

                                }
                            }
                        }
                        GUI._GUI1.update("File \"" + file.Name + "\" completed\n");
                    }
                    catch (FormatException ex) {
                        GUI._GUI1.update("Wrong file format.");
                    }
                    catch (OutOfMemoryException) {
                        GUI._GUI1.update("Out of memory");
                    }
                }

            }
        }
    }
}

以及我从行中放入值的对象: 入口.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace STI.entities_package {
    class Entry {
        private long date;
        private int location;
        private float value;
        private String fileName;
        private int count;

        public Entry(long date, int location, float value, String fileName) {
            this.date = date;
            this.location = location;
            this.value = value;
            this.fileName = fileName;

            this.count = 1;
        }

        public long getDate() {
            return date;
        }

        public int getLocation() {
            return location;
        }

        public String getFileName() {
            return fileName;
        }

    }
}

【问题讨论】:

  • TL;博士; MCVE
  • 多线程在这里肯定帮不了你,因为引入更多线程并不会神奇地为你的进程增加更多内存。编辑问题,以便删除对多线程的任何提及。同时删除 GUI 代码,因为这不是问题。
  • 内存不足是一个相当愚蠢的问题,现代计算机有很多。 1 TB 非常容易获得,而且也不花钱。 Project + Properties,Build选项卡,将Platform target设置更改为AnyCPU,看到“Prefer 32-bit”选项时取消勾选。你不喜欢它。
  • 我已经清理了代码。那么不应该有任何类型的多线程吗?就像我说的,如果我可以在一个线程中从容器中读取并同时写入新值,它会“给我更多内存”,因为我不必再存储这些东西了,对吧?但是频率有问题。
  • @HansPassant 你是什么意思?所有对象在 32b 和 64b 中都有 2GB 大小的限制。我指的是(stackoverflow.com/questions/3657181/…)。此外,我应该运行这个应用程序的计算机根本不是现代的。它有 Intel Core 2 Duo L7500 和 3GB Ram

标签: c# multithreading winforms thread-safety


【解决方案1】:

我认为多线程在这里不会对您有所帮助 - 它可以帮助您将 IO 密集型任务与 CPU 密集型任务分开,但您的 CPU 密集型任务是如此微不足道,以至于我认为它们不会保证自己的线程。所有多线程都会不必要地增加问题的复杂性。

计算常量内存中的最小值/最大值很简单:只需维护一个 minFile 和 maxFile 变量,当当前文件的值小于 minFile 或大于 maxFile 时,该变量会更新。查找最频繁的值将需要更多内存,但只有几百万个文件,您应该有足够的 RAM 来存储保持每个值的频率的Dictionary&lt;float, int&gt;,然后遍历映射以确定哪个值频率最高。如果由于某种原因您没有足够的 RAM(如果您的内存不足,请确保您的文件正在关闭并进行垃圾收集,因为具有几百万个条目的 Dictionary&lt;float, int&gt; 应该适合不到一个千兆字节RAM)然后您可以对文件进行多次传递:在第一次传递时将值存储在Dictionary&lt;interval, int&gt; 中,您将 MIN_FLOAT 和 MAX_FLOAT 之间的间隔分成几千个子间隔,然后在下一次传递您可以忽略所有不适合频率最高的区间的值,从而缩小字典的大小。但是,Dictionary&lt;float, int&gt; 应该适合内存,因此除非您开始处理数十亿个文件而不是数百万个文件,否则您可能不需要多通道过程。

【讨论】:

  • 很有趣,谢谢。我会试试看。你是对的,我可以将值存储在字典中,因为即使最大的文件有大约 45M 行,也可能不会有那么多唯一值,所以字典可以满足限制,而不是在那里存储每一行​​。顺便提一句。没有那么多文件。我有大约 5-7 个文件,它们只是大小不同。
  • @Arcane Gotcha,我认为这是每个值一个文件
猜你喜欢
  • 1970-01-01
  • 2017-03-07
  • 2013-10-23
  • 1970-01-01
  • 1970-01-01
  • 2016-05-26
  • 1970-01-01
  • 2016-12-21
  • 2014-09-30
相关资源
最近更新 更多