使用委托的异步调用

本文关键字:异步 调用 | 更新日期: 2023-09-27 17:49:36

我希望方法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);
     }
}

使用委托的异步调用

您的代码实际上并不需要任何多线程。它实际上甚至不需要那么多的异步处理——你很可能会使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

您不需要调用EndInvoke,实际上所有EndInvoke所做的就是等待返回值。由于SplitFile返回void,我猜测这是一个优化,因为你不需要等待任何东西,它只是忽略了等待。了解更多细节:没有EndInvoke的c#异步调用?

也就是说,使用Begin/EndInvoke可能不会比串行编程快(而且可能会稍微慢一点),因为for循环仍然是串行的,并且仍然以串行方式运行迭代。唯一改变的是你使用了一个委托而它看起来是不必要的。

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

编辑:正如其他人提到的,有多个线程参与文件操作可能不会提高性能,因为文件操作可能是磁盘绑定的。从异步文件读/写中获得的主要好处可能是为UI更新解除主线程阻塞。如果你想要一个更好的答案,你需要指定你想要的"性能"。