【问题标题】:Threading with writing to file system [closed]写入文件系统的线程[关闭]
【发布时间】:2015-03-20 03:52:40
【问题描述】:

我有这个。这是一个生成银行账户的应用程序

static void Main(string[] args)
    {

        string path = @"G:\BankNumbers";
        var bans = BankAcoutNumbers.BANS;
        const int MAX_FILES = 80;
        const int BANS_PER_FILE = 81818182/80;
        int bansCounter = 0;
        var part = new List<int>();
        var maxNumberOfFiles = 10;
        Stopwatch timer = new Stopwatch();
        var fileCounter = 0;


        if (!Directory.Exists(path))
        {
            DirectoryInfo di = Directory.CreateDirectory(path);
        }

        try
        {
            while (fileCounter <= maxNumberOfFiles)
            {
                timer.Start();
                foreach (var bank in BankAcoutNumbers.BANS)
                {
                    part.Add(bank);
                    if (++bansCounter >= BANS_PER_FILE)
                    {
                        string fileName = string.Format("{0}-{1}", part[0], part[part.Count - 1]);
                        string outputToFile = "";// Otherwise you dont see the lines in the file. Just single line!!

                        Console.WriteLine("NR{0}", fileName);
                        string subString = System.IO.Path.Combine(path, "BankNumbers");//Needed to add, because otherwise the files will not stored in the correct folder!!
                        fileName =  subString + fileName;

                        foreach (var partBan in part)
                        {

                            Console.WriteLine(partBan);
                            outputToFile += partBan + Environment.NewLine;//Writing the lines to the file

                        }
                        System.IO.File.WriteAllText(fileName, outputToFile);//Writes to file system.
                        part.Clear();
                        bansCounter = 0;
                        //System.IO.File.WriteAllText(fileName, part.ToString());

                        if (++fileCounter >= MAX_FILES)
                            break;
                    }
                }
            }

            timer.Stop();
            Console.WriteLine(timer.Elapsed.Seconds);
        }
        catch (Exception)
        {

            throw;
        }

        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();
    }

但这会生成 8100 万条银行账户记录,这些记录分为 80 多个文件。但是我可以用线程加速这个过程吗?

【问题讨论】:

  • 可能不会,不。无论哪种方式,欢迎您尝试并自己找出答案。这是获得确凿答案的最佳方式。
  • 在循环中使用 StringBuilder 而不是字符串连接。或者干脆File.WriteAllLines(fileName, part) 消除循环。

标签: c#


【解决方案1】:

你的过程可以分为两个步骤:

  1. 创建一个帐户
  2. 将帐户保存在文件中

第一步可以并行完成,因为帐户之间没有依赖关系。即创建一个帐号xyz,您不必依赖来自xyz - 1 帐户的数据(因为它可能尚未创建)。

问题在于将数据写入文件。您不希望多个线程尝试访问和写入同一个文件。添加锁可能会使您的代码成为维护的噩梦。另一个问题是写入文件会减慢整个过程。

目前,在您的代码中创建帐户和写入文件发生在一个进程中。

您可以尝试将这些进程分开。因此,首先您创建所有帐户并将它们保存在某个集合中。这里可以安全地使用多线程。只有在创建了所有帐户后,您才能保存它们。

改进保存过程需要更多的工作。您必须将所有帐户分成 8 个单独的集合。您为每个集合创建一个单独的文件。然后您可以获取第一个集合,第一个文件,并创建一个将数据写入文件的线程。第二个集合和第二个文件也是如此。等等。这 8 个进程可以并行运行,您不必担心会有多个线程尝试访问同一个文件。

下面是一些伪代码来说明这个想法:

    public void CreateAndSaveAccounts()
    {
        List<Account> accounts = this.CreateAccounts();

        // Divide the accounts into separate batches
        // Of course the process can (and shoudl) be automated.
        List<List<Account>> accountsInSeparateBatches =
            new List<List<Account>>
            {
                accounts.GetRange(0, 10000000),             // Fist batch of 10 million
                accounts.GetRange(10000000, 10000000),      // Second batch of 10 million
                accounts.GetRange(20000000, 10000000)       // Third batch of 10 million
                // ...
            };

        // Save accounts in parallel
        Parallel.For(0, accountsInSeparateBatches.Count,
            i =>
                {
                    string filePath = string.Format(@"C:\file{0}", i);
                    this.SaveAccounts(accountsInSeparateBatches[i], filePath);
                }
            );
    }

    public List<Account> CreateAccounts()
    {
        // Create accounts here
        // and return them as a collection.
        // Use parallel processing wherever possible
    }

    public void SaveAccounts(List<Account> accounts, string filePath)
    {
        // Save accounts to file
        // The method creates a thread to do the work.
    }

【讨论】:

    【解决方案2】:

    您说的是加快一个进程,其瓶颈极有可能是文件写入速度。您无法真正有效地并行写入单个磁盘。

    如果您生成一个仅负责 fileIO 的工作线程,您可能会看到速度略有提高。换句话说,创建一个缓冲区,让您的主线程将内容转储到其中,而另一个线程将其写入磁盘。这是经典的生产者/消费者动态。不过,我预计不会有明显的速度提升。

    还要记住,写入控制台会减慢您的速度,但您可以将其保留在主线程中,您可能会没事的。只需确保对缓冲区大小进行了限制,并在缓冲区已满时让生产者线程挂起。

    编辑:还可以查看 L-Three 提供的链接,使用 BufferedStream 会有所改进(并且可能不需要使用消费者线程)

    【讨论】:

    • 感谢您的评论。能给我举个例子吗。谢谢你。但是关键字 Async - 是不是可以加快速度?
    • 这里的问题是将文件写入磁盘。您可以使用多个线程来生成要存储的数据,但您已经生成数据的速度比将其写入磁盘的速度要快。如果你加快速度,队列只会填得更快。我的专长不是 C#,但我怀疑它已经是缓冲输出,这意味着您可能应该期望从任何多线程中获得最小的收益。
    • @Nielsfischerein 不,不是。这是一个使编写异步代码更容易的关键字。就是这样。
    • 但是你可以用线程来做到这一点,为每个文件创建一个线程?但是怎么做呢?
    猜你喜欢
    • 2017-08-24
    • 2012-02-27
    • 2015-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多