为什么以下仅在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。
答案 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(); });
}
}