为什么StreamWriter.AutoFlush使这个打印两次?

时间:2016-01-28 22:46:13

标签: c# c#-4.0

为什么以下仅在StreamWriter.AutoFlush设置为true时打印重复邮件?

using (var stream = log.Open(FileMode.Append, FileAccess.Write, FileShare.Read))
{
    using (var streamWriter = new StreamWriter(stream))
    {
        /****************************/
        streamWriter.AutoFlush = true;
        /****************************/
        streamWriter.WriteLine(new string('-', 80));
        streamWriter.WriteLine(file.FullName);
        streamWriter.WriteLine(new string('-', 80));
        try
        {
            await Runner.Run(
                file, // Argument passed in. In this case, points to ConsoleApplication10.exe
                streamWriter,
                streamWriter);
        }
        catch (Exception ex)
        {
            streamWriter.WriteLine(ex.ToString());
            throw;
        }
    }
}

这是.AutoFlush=true被注释掉时的文件内容:

--------------------------------------------------------------------------------
(path removed for security)\ConsoleApplication10.exe
--------------------------------------------------------------------------------
Out: 1/28/2016 5:19:22 PM
Err: 1/28/2016 5:19:22 PM

这是.AutoFlush=true时的文件内容。请注意重复的行:

--------------------------------------------------------------------------------
(path removed for security)\ConsoleApplication10.exe
--------------------------------------------------------------------------------
Out: 1/28/2016 5:11:59 PM
Out: 1/28/2016 5:11:59 PM
Err: 1/28/2016 5:11:59 PM

ConsoleApplication10.exe的源代码中没有任何意外:

static void Main(string[] args)
{
    Console.Out.WriteLine("Out: " + DateTime.Now.ToString());
    Console.Error.WriteLine("Err: " + DateTime.Now.ToString());
}

此处Runner.Run。请注意,它在与当前用户不同的凭据集下启动进程。除此之外,它只是管道stderr和stdout类似于https://msdn.microsoft.com/en-us/library/system.diagnostics.process.outputdatareceived(v=vs.110).aspx中给出的示例:

public static async Task Run(FileInfo executable, TextWriter writerForStandardOutput, TextWriter writerForStandardError)
{
    if (!executable.Exists)
        throw new Exception("The given executable doesn't exist");

    // Start the process
    using (var process = new Process())
    {
        // Set up the process's start info
        process.StartInfo = new ProcessStartInfo()
        {
            CreateNoWindow = true,
            Domain = DOMAIN, // The domain needs to be specified or you'll get an error that says "The stub received bad data"
            FileName = executable.FullName,
            LoadUserProfile = true, // Set to true so that functionality isn't unnecessarily limited for the credentials this program will run under
            Password = PASSWORD,
            RedirectStandardError = true, // We'll be capturing errors
            RedirectStandardOutput = true, // We'll be capturing output
            UserName = USERNAME,
            UseShellExecute = false, // Can't specify credentials to use unless this is set to false
            WindowStyle = ProcessWindowStyle.Hidden
        };

        // Set up listeners for when output or error data is received
        process.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
        {
            if (!ReferenceEquals(e.Data, null))
                writerForStandardOutput.WriteLine(e.Data);
        });
        process.ErrorDataReceived += new DataReceivedEventHandler((sender, e) =>
        {
            if (!ReferenceEquals(e.Data, null))
                writerForStandardError.WriteLine(e.Data);
        });

        // Try to start the executable
        process.Start();
        // Begin redirecting stdout and stderr
        process.BeginOutputReadLine();
        process.BeginErrorReadLine();

        // Wait for the process to end
        await Task.Run(() => { process.WaitForExit(); });
    }
}

我四处使用.Net 4.6。

1 个答案:

答案 0 :(得分:1)

@ usr对这个问题的评论是正确的。根本原因是以非线程安全的方式写入streamWriter。将Runner.Run更改为此修复了此问题。请注意是否存在lock语句以使写入同步:

public static async Task Run(FileInfo executable, TextWriter writerForStandardOutput, TextWriter writerForStandardError)
{
    if (!executable.Exists)
        throw new Exception("The given executable doesn't exist");

    // Start the process
    using (var process = new Process())
    {
        // Set up the process's start info
        process.StartInfo = new ProcessStartInfo()
        {
            CreateNoWindow = true,
            Domain = DOMAIN, // The domain needs to be specified or you'll get an error that says "The stub received bad data"
            FileName = executable.FullName,
            LoadUserProfile = true, // Set to true so that functionality isn't unnecessarily limited for the credentials this program will run under
            Password = PASSWORD,
            RedirectStandardError = true, // We'll be capturing errors
            RedirectStandardOutput = true, // We'll be capturing output
            UserName = USERNAME,
            UseShellExecute = false, // Can't specify credentials to use unless this is set to false
            WindowStyle = ProcessWindowStyle.Hidden
        };

        // Create an object for synchronizing writes to the output and error TextWriter objects
        var lockObject = new object();

        // Set up listeners for when output or error data is received
        process.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
        {
            if (!ReferenceEquals(e.Data, null))
            {
                lock (lockObject)
                {
                    writerForStandardOutput.WriteLine(e.Data);
                }
            }
        });
        process.ErrorDataReceived += new DataReceivedEventHandler((sender, e) =>
        {
            if (!ReferenceEquals(e.Data, null))
            {
                lock (lockObject)
                {
                    writerForStandardError.WriteLine(e.Data);
                }
            }
        });

        // Try to start the executable
        process.Start();
        // Begin redirecting stdout and stderr
        process.BeginOutputReadLine();
        process.BeginErrorReadLine();

        // Wait for the process to end
        await Task.Run(() => { process.WaitForExit(); });
    }
}