在c#中异步读取三个文件

时间:2013-08-26 00:14:09

标签: c#

我正在异步读取三个文件并将它们写入一个文件中。 问题是它只写入LAST文件的内容。这是为什么? 我的代码:主要类:

    public static FileReader filereader1=  new FileReader("file1.txt");
    public static FileReader filereader2 = new FileReader("file2.txt");
    public static FileReader filereader3 = new FileReader("file3.txt");
    public static FileWriter towrite = new FileWriter(writefile.txt");

public static void readFileAsync(object obj)
{
  var filereader = obj as FileReader;
  readf = new ReadFile(filereader.readLine);
  if (filereader!=null)
  {
    while (!filereader.IsFinished)
    {
      IAsyncResult ar = readf.BeginInvoke(new AsyncCallback(myCallback),null);
    }

   }
 }
public static void myCallback(IAsyncResult iar)
{
  AsyncResult ar = (AsyncResult)iar;
  ReadFile readf = (ReadFile)ar.AsyncDelegate;
  string line = readf.EndInvoke(iar);
  writefile.writeLine(line);
 }
 static void Main(string[] args)
 {
    readFileAsync(filereader1);
    readFileAsync(filereader2);
    readFileAsync(filereader3);
    Console.ReadKey();
    filereader1.Dispose();
    filereader2.Dispose();
    filereader3.Dispose();
    writefile.Dispose();

} class FileReader:

    public static StreamReader toRead { get; set; }
    public string FilePath { get; set; }

    public FileReader(string path)
    {
        FilePath = path;
        toRead = new StreamReader(path);
    }
    public string ReadLine()
    {
        string line=null;

            if (!toRead.EndOfStream)
            {
                line = toRead.ReadLine();
                return line;
            }
            return null;


    }
    public bool IsFinished
    {
        get
        {
            if (toRead != null)
            {
                return toRead.EndOfStream;
            }
            return true;
       }

    }
    public void Dispose()
    {
        if (toRead != null)
        {
            toRead.Close();
        }
    }
}

类FileWriter:       {

    public StreamWriter toWrite { get; set; }
    public static object locker = new object();
    public string FilePath { get; set; }

    public FileWriter(string path)
    {
        FilePath = path;
        toWrite = new StreamWriter(path);
    }

    public void writeLine(string line)
    {
        if (toWrite != null)
        {
            lock (locker)
            {
                toWrite.WriteLine(line);
            }
        }
    }

    public void Dispose()
    {
        if (toWrite != null)
        {
            toWrite.Close();
        }
    }
}

2 个答案:

答案 0 :(得分:1)

将writefile设为单个实例,并在编写内容时应用同步锁。

public static void myCallback(IAsyncResult iar)
{
  AsyncResult ar = (AsyncResult)iar;
  ReadFile readf = (ReadFile)ar.AsyncDelegate;
  string line = readf.EndInvoke(iar);
  lock(obj)
  {
    writefile.writeLine(line);
    successCount++;
  }
 }
private static object obj = new object();
private static int successCount = 0;
static void Main(string[] args)
 {
    readFileAsync(filereader1);
    readFileAsync(filereader2);
    readFileAsync(filereader3);
    while(Console.ReadKey())
    {
        if(successCount == 3)
        {
            filereader1.Dispose();
            filereader2.Dispose();
            filereader3.Dispose();
            writefile.Dispose();
            break;
        }
    }
}

答案 1 :(得分:0)

是否有理由需要使用异步回调?

如果没有,请尝试这些方法。

TPL:

var tasks = new []
{
    Task.Factory.StartNew(() => File.ReadAllText(@"file1.txt")),
    Task.Factory.StartNew(() => File.ReadAllText(@"file2.txt")),
    Task.Factory.StartNew(() => File.ReadAllText(@"file3.txt")),
};

Task.Factory.ContinueWhenAll(tasks, ts =>
{
    var output = @"writefile.txt";
    File.WriteAllText(output, ts[0].Result);
    File.AppendAllText(output, ts[1].Result);
    File.AppendAllText(output, ts[2].Result);
    Console.WriteLine("Done.");
});

Microsoft Reactive Framework(Rx):

var write = @"writefile.txt";
File.WriteAllText(write, null);

new []
{
    Observable.Start(() => File.ReadAllText(@"file1.txt")),
    Observable.Start(() => File.ReadAllText(@"file2.txt")),
    Observable.Start(() => File.ReadAllText(@"file3.txt")),
}
    .Merge()
    .Subscribe(
        x => File.AppendAllText(write, x),
        () => Console.WriteLine("Done."));

两者都异步读取文件并同步写入文件(没有覆盖!)。 TPL解决方案按文件顺序写入结果。 Rx保留文件读取的顺序。


您的代码在以下几行中存在严重问题:

while (!filereader.IsFinished)
{
    IAsyncResult ar = readf.BeginInvoke(new AsyncCallback(myCallback),null);
}

异步尝试读取行,但它正在同步尝试确定您是否已经到达文件的末尾。这将尝试在第一个从文件读取之前触发许多异步调用。这段代码永远不会起作用。

您的代码的另一个问题是,FileReader类中定义流的行已声明为static。这需要定义如下:

public StreamReader toRead { get; set; }

现在我重新编写了代码来生成相当好的代码:

public static void readFileAsync(FileReader filereader)
{
    var readf = new ReadFile(filereader.ReadLine);
    Action<IAsyncResult> callBack = null;
    callBack = iar =>
    {
        var line = readf.EndInvoke(iar);
        writefile.writeLine(line);
        if (!filereader.IsFinished)
        {
            readf.BeginInvoke(new AsyncCallback(callBack), null);
        }
    };
    if (!filereader.IsFinished)
    {
        readf.BeginInvoke(new AsyncCallback(callBack), null);
    }
}

只有在前一个读完成后才调用新的读取。

我也进行了彻底的重新设计,并将异步性更改为基于文件的,而不是基于行的。这也有效:

    static void Main(string[] args)
    {
        using (var writefile = new FileWriter(@"writefile.txt"))
        {
            Action<string> readAndWrite= fn =>
            {
                using (var fr = new FileReader(fn))
                {
                    while (!fr.IsFinished)
                    {
                        writefile.writeLine(fr.ReadLine());
                    }
                }
            };

            AsyncCallback callBack = ar => { };

            var ar1 = readAndWrite.BeginInvoke(@"file1.txt", callBack, null);
            var ar2 = readAndWrite.BeginInvoke(@"file2.txt", callBack, null);
            var ar3 = readAndWrite.BeginInvoke(@"file3.txt", callBack, null);

            WaitHandle.WaitAll(new[]
            {
                    ar1.AsyncWaitHandle,
                    ar2.AsyncWaitHandle,
                    ar3.AsyncWaitHandle,
            });
        }
    }
}

希望这些建议有所帮助。