【问题标题】:C# concurrent filestream read/write eofC#并发文件流读/写eof
【发布时间】:2014-07-08 08:42:11
【问题描述】:

我有一个线程在日志文件中生成行:

var t1 = Task.Factory.StartNew(() =>
{
    using (var fileStream = File.Open(file, FileMode.Create, FileAccess.Write, FileShare.Read))
    using (var streamWriter = new StreamWriter(fileStream))
    {
        for (var i = 0; i < 10; i++)
        {
            streamWriter.WriteLine(i);
            streamWriter.Flush();
            Thread.Sleep(1000);
        }
    }

    File.Delete(file);
});

我有一个线程从同一个日志文件中读取行:

// Reads lines from the log file.
var t2 = Task.Factory.StartNew(() =>
{
    Thread.Sleep(500); // Horrible wait to ensure file existence in this test case.
    using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read | FileShare.Write))
    using (var streamReader = new StreamReader(fileStream))
    {
        string line;
        while ((line = streamReader.ReadLine()) != null) Console.WriteLine(line);
        // FIXME: The stream reader stops, instead of doing a continous read.
        Console.WriteLine("End of file");
    }
});

阅读器应该阅读所有写入的行,因此它应该等待更多数据,而不是在第一次遇到 EOF 时停止。我不介意读者是否永远不会“完成”,只要文件继续存在,读者就可以继续阅读。我怎样才能做到这一点?用于复制目的的完整代码:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace PlayGround
{
    internal static class Program
    {
        private static void Main()
        {
            const string file = "test.log";

            // Writes lines to the log file.
            var t1 = Task.Factory.StartNew(() =>
            {
                using (var fileStream = File.Open(file, FileMode.Create, FileAccess.Write, FileShare.Read))
                using (var streamWriter = new StreamWriter(fileStream))
                {
                    for (var i = 0; i < 10; i++)
                    {
                        streamWriter.WriteLine(i);
                        streamWriter.Flush();
                        Thread.Sleep(1000);
                    }
                }

                File.Delete(file);
            });

            // Reads lines from the log file.
            var t2 = Task.Factory.StartNew(() =>
            {
                Thread.Sleep(500); // Horrible wait to ensure file existence in this test case.
                using (
                    var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read,
                        FileShare.Delete | FileShare.Read | FileShare.Write))
                using (var streamReader = new StreamReader(fileStream))
                {
                    string line;
                    while ((line = streamReader.ReadLine()) != null) Console.WriteLine(line);
                    // FIXME: The stream reader stops, instead of doing a continous read.
                    Console.WriteLine("End of file");
                }
            });

            Task.WaitAll(t1, t2);
        }
    }
}

编辑:作为一个实际示例,这对于第三方进程正在生成需要读取和处理的日志条目的场景很有用。如果这使应用程序和使用更清晰,您可以将其视为日志文件阅读器。

【问题讨论】:

  • 嗯,EOF 那里,所以我想你只需要在一个循环中继续尝试ReadLine 就可以了。可能有更好的方法,但这应该可行。

标签: c# .net multithreading filestream streamreader


【解决方案1】:

您可以在line == null 时执行等待,方法是检查streamReader.EndOfFile 属性。使用Thread.Sleep(1000) 不是一个理想的解决方案,有点老套,我想还有其他更好的替代解决方案。 :-)

using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read | FileShare.Write))
using (var streamReader = new StreamReader(fileStream))
{
    string line;
    bool running = true; // we may want to terminate this loop in some condition.
    while (running)
    {
        line = streamReader.ReadLine();
        if (line != null)
        {
            Console.WriteLine(line);
        }
        else // as per edit, the whole else block can be omitted.
        {
            while (streamReader.EndOfStream)
            {
                Thread.Sleep(1000); // wait around for n time. This could end up in an infinte loop if the file is not written to anymore. 
            }
        }
   }

   // FIXME: The stream reader stops, instead of doing a continous read.
   Console.WriteLine("End of file");
}

编辑:你可以不用 else 块:

else 
{ 
    while (streamReader.EndOfStream) 
    { 
        Thread.Sleep(1000) 
    } 
}

像这样:

while (running)
{
    line = streamReader.ReadLine();
    if (line != null)
    {
        Console.WriteLine(line);
    }
}

【讨论】:

  • 我喜欢这种方法,但这实际上是轮询文件而不是阻塞调用。如果存在用于确定何时可以读取下一个读取行的阻塞调用,那将是完美的解决方案。
  • 由于第 3 方的原因,无法选择使用 AutoResetEvent?
  • 没错。无法控制作者。
【解决方案2】:

您需要同步机制,在这种情况下我使用 AutoResetEvent。 根据您的代码所需的更改是。

const string file = "test.log";
// Adds new line.
AutoResetEvent signal = new AutoResetEvent(false);

streamWriter.Flush();
// Adds new line.
signal.Set();

File.Delete(file);
// Adds new line
signal.Set();

Thread.Sleep(500);
// Replaces with.
signal.WaitOne();

while ((line = streamReader.ReadLine()) != null) Console.WriteLine(line);
// Replaces with.
while ((line = streamReader.ReadLine()) != null)
{
    signal.WaitOne();
    Console.WriteLine(line);
}

完整代码

const string file = "test.log";
AutoResetEvent signal = new AutoResetEvent(false);

// Writes lines to the log file.
var t1 = Task.Factory.StartNew(() =>
{
    using (var fileStream = File.Open(file, FileMode.Create, FileAccess.Write, FileShare.Read))
    {
        using (var streamWriter = new StreamWriter(fileStream))
        {
            for (var i = 0; i < 10; i++)
            {
                streamWriter.WriteLine(i);
                streamWriter.Flush();
                signal.Set();
                Thread.Sleep(10);
            }
        }
    }

    File.Delete(file);
    signal.Set();
});

// Reads lines from the log file.
var t2 = Task.Factory.StartNew(() =>
{
    signal.WaitOne();
    using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read | FileShare.Write))
    {
        using (var streamReader = new StreamReader(fileStream))
        {
            string line;
            while ((line = streamReader.ReadLine()) != null)
            {
                signal.WaitOne();
                Console.WriteLine(line);
            }

            // FIXME: The stream reader stops, instead of doing a continous read.
            Console.WriteLine("End of file");
        }
    }
});

Task.WaitAll(t1, t2);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-10
    相关资源
    最近更新 更多