【问题标题】:Asynchronous Call using Delegate使用委托的异步调用
【发布时间】:2015-05-19 05:31:10
【问题描述】:

我希望方法 splitFile 的单独异步线程应该运行,以便任务变得更快,但下面的代码不起作用。当我调试时,它到达RecCnt = File.ReadAllLines(SourceFile).Length - 1; 行并出来。请帮忙。

public delegate void SplitFile_Delegate(FileInfo file);

static void Main(string[] args)
{ 
     DirectoryInfo d = new DirectoryInfo(@"D:\test\Perf testing Splitter"); //Assuming Test is your Folder
     FileInfo[] Files = d.GetFiles("*.txt"); //Getting Text files

     foreach (FileInfo file in Files)
     {
         SplitFile_Delegate LocalDelegate = new SplitFile_Delegate(SplitFile);

         IAsyncResult R = LocalDelegate.BeginInvoke(file, null, null); //invoking the method
         LocalDelegate.EndInvoke(R);

      }
}

private static void SplitFile(FileInfo file)
{
     try
     {
         String fname;
         //int FileLength;
         int RecCnt;
         int fileCount;
         fname = file.Name;
         String SourceFile = @"D:\test\Perf testing Splitter\" + file.Name;


         RecCnt = File.ReadAllLines(SourceFile).Length - 1;
         fileCount = RecCnt / 10000;

         FileStream fs = new FileStream(SourceFile, FileMode.Open);
         using (StreamReader sr = new StreamReader(fs))
         {
             while (!sr.EndOfStream)
             {
                 String dataLine = sr.ReadLine();
                 for (int x = 0; x < (fileCount + 1); x++)
                 {
                      String Filename = @"D:\test\Perf testing Splitter\Destination Files\" + fname + "_" + x + "by" + (fileCount + 1) + ".txt"; //test0by4
                      using (StreamWriter Writer = file.AppendText(Filename))
                      {
                          for (int y = 0; y < 10000; y++)
                          {
                              Writer.WriteLine(dataLine);
                              dataLine = sr.ReadLine();
                          }
                          Writer.Close();
                      }

                  }
              }
          }
     }
     catch (Exception ex)
     {
          Console.WriteLine(ex.Message);
     }
}

【问题讨论】:

  • “出来了”是什么意思?例外?阻止?
  • 它执行成功但没有给出任何结果(不拆分文件)。在那一行之后它返回到主函数,再次返回到具有下一个文件名的拆分文件并再次执行相同操作。
  • 作为旁注:一般async != faster
  • 紧跟在BeginInvoke 之后的EndInvoke 是我见过的最糟糕的异步反模式——你想在这里做什么?
  • 您的代码实际上是否已使 CPU 内核完全饱和?我很确定你会受到 I/O 的限制,而不是 CPU 负载(除非你有一个相当大的 SSD RAID 阵列:))。在问题上投入更多线程不会有帮助 - 你能做的最好的事情就是消除ReadAllLines 和实际工作之间的 CPU 时间。但是,如果您只是不读取文件两次,您将节省更多。您已经拥有内存中的所有行,那么为什么要扔掉它们并再次处理文件呢?使用这种愚蠢的阅读模式? ReadLine 在最后一行返回 null,无需检查 EOF :)

标签: c# asynchronous delegates


【解决方案1】:

您的代码实际上并不需要任何多线程。它甚至不需要那么多的异步处理——你最有可能使 I/O 饱和,除非你有多个驱动器作为数据源,否则你不会通过添加并行性来改善它。

另一方面,您的代码读取每个文件两次。无缘无故地浪费内存、时间甚至 CPU。相反,只需这样做:

FileStream fs = new FileStream(SourceFile, FileMode.Open);
using (StreamReader sr = new StreamReader(fs))
{
    string line;
    string fileName = null; 
    StreamWriter outputFile = null;
    int lineCounter = 0;
    int outputFileIndex = 0;

    while ((line = sr.ReadLine()) != null)
    {
        if (fileName == null || lineCounter >= 10000)
        {
            lineCounter = 0;
            outputFileIndex++;
            fileName = @"D:\Output\" + fname + "_" + outputFileIndex + ".txt";

            if (outputFile != null) outputFile.Dispose();
            outputFile = File.AppendText(fileName);
        }

        outputFile.WriteLine(line);
        lineCounter++;
    }
}

如果你真的需要XOutOfY 格式的文件名,你可以在之后重命名它们 - 这比逐行读取源文件两次要便宜得多。或者,如果您不关心一次将整个文件保存在内存中,只需使用从 ReadAllLines 获得的数组并对其进行迭代,而不是重新读取。

为了更简单,您还可以使用foreach (var line in File.ReadLines(fileName))

如果你真的想让这个异步,处理它的方法是使用异步 I/O,而不仅仅是通过假脱机新线程。所以你可以使用awaitStreamReader.ReadLineAsync 等。

【讨论】:

  • 错误:不能为 linecounter 和 outputfile 使用未分配的变量。
  • @Mandar 现在已修复。
  • 谢谢卢安。有效。这些行到底在做什么: if (outputFile != null) outputFile.Dispose(); outputFile = File.AppendText(fileName);
  • @Mandar 第一个确保正确关闭旧的(和完成的)输出文件。第二个然后为下一批行创建(嗯,“附加到” - 你可能想要重新考虑)一个新文件。然后循环的其余部分只需要关心将一行写入文件,而不必关心它实际上是什么文件。
  • 谢谢卢安。现在我想将此代码放在单独的方法中。(或说线程)。并希望同时运行该方法的多个实例,以减少执行时间。有没有可能实现这一点?
【解决方案2】:

您不需要调用 EndInvoke,实际上所有 EndInvoke 所做的只是等待您的返回值。由于 SplitFile 返回 void,我猜是有一个优化开始了,因为你不需要等待任何东西,它只是忽略了等待。更多详情:C# Asynchronous call without EndInvoke?

话虽如此,您对 Begin/EndInvoke 的使用可能不会比串行编程快(并且可能会稍微慢一些),因为您的 for 循环仍然是序列化的,并且您仍在串行运行迭代。改变的只是你使用了一个看起来不需要的委托。

您打算使用的可能是 Parallel.ForEach(MSDN:https://msdn.microsoft.com/en-us/library/dd992001(v=vs.110).aspx),它可能会并行运行迭代。

编辑:正如其他人所提到的,让多个线程参与文件操作可能不会提高性能,因为您的文件操作可能是磁盘绑定的。从异步文件读/写中获得的主要好处可能是取消阻塞主线程以进行 UI 更新。如果您想要一个更好的答案,您需要用“性能”来指定您想要什么。

【讨论】:

  • 理想情况下,文件分割的所有线程都应该独立并行运行。
猜你喜欢
  • 2017-06-23
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 2014-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多