C#从重定向的标准输出异步读取数据
我正在开发一个C#WFA,它允许我用一个化学软件做一些事情:GAMESS 基本上我有一个批处理文件,当使用适当的参数执行时,它返回一个包含原子分析所需数据的文件 在我的应用程序表单中,我设置了一个只读文本框,其中包含进程的输出。我通过重定向标准输出来实现这一点。此读取以同步方式完成。对于小输出,效果很好,但很多时候,线weel超过240k。我的进程在结束时自动关闭(最后一条指令是“退出”,而不是“退出/B”) 正如您(可能)发现的那样,主窗体变得不稳定,在进程结束之前不允许任何用户输入。这是一个问题 我曾尝试在代码中为不同的函数异步读取文件流,效果很好,但我一直坚持标准输出。我想不出正确的方法 以下是我迄今为止的代码:C#从重定向的标准输出异步读取数据,c#,asynchronous,redirectstandardoutput,C#,Asynchronous,Redirectstandardoutput,我正在开发一个C#WFA,它允许我用一个化学软件做一些事情:GAMESS 基本上我有一个批处理文件,当使用适当的参数执行时,它返回一个包含原子分析所需数据的文件 在我的应用程序表单中,我设置了一个只读文本框,其中包含进程的输出。我通过重定向标准输出来实现这一点。此读取以同步方式完成。对于小输出,效果很好,但很多时候,线weel超过240k。我的进程在结束时自动关闭(最后一条指令是“退出”,而不是“退出/B”) 正如您(可能)发现的那样,主窗体变得不稳定,在进程结束之前不允许任何用户输入。这是一个
private void Button1_Click(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(Input_job_file) && !String.IsNullOrEmpty(Log_file))
{
textBox3.Text = "Executing code...\r\n";
string outtext;
string batpath = string.Format(Settings.Default.path_to_gamess + "\\rungms.bat");
string arguments = string.Format("{0} {1} {2} 0 {3}", Input_job_file, Settings.Default.version, Ncpus, Log_file);
//Here we need to copy the input file to the gamess directory in order to avoid errors
File.Copy(Input_job_file, Settings.Default.path_to_gamess + "\\" + FileNameWithoutExtension + ".inp");
Process gamessjob = new Process();
gamessjob.StartInfo.ErrorDialog = true;
gamessjob.StartInfo.UseShellExecute = false;
gamessjob.StartInfo.CreateNoWindow = true;
gamessjob.StartInfo.RedirectStandardOutput = true;
gamessjob.StartInfo.RedirectStandardError = true;
gamessjob.EnableRaisingEvents = true;
gamessjob.StartInfo.WorkingDirectory = Settings.Default.path_to_gamess;
gamessjob.StartInfo.FileName = batpath;
gamessjob.StartInfo.Arguments = arguments; //input_file, version, number_of_processors, "0", output_name]
gamessjob.Start();
//STDOUT redirection to our textbox in readonly mode
outtext = gamessjob.StandardOutput.ReadToEnd();
textBox3.Text += outtext + "\r\nProcess executed!";
//here we clean up some stuff after GAMESS code ends
File.Delete(Settings.Default.path_to_gamess + "\\" + FileNameWithoutExtension + ".inp");
MessageBox.Show("Code executed!\nCheck output for errors or messages.", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else MessageBox.Show("Input file and/or output location is invalid!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
//ASYNC
byte[] result;
textBox6.Text = "Executing code...\r\n\n";
using (FileStream SourceStream = File.Open(Input_dat_file, FileMode.Open))
{
result = new byte[SourceStream.Length];
await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length);
}
理想情况下,以异步方式读取重定向的standardoutput可以解决我的应用程序的死锁(对吗?),或者至少可以缩短死锁的持续时间。也许如果我使用ReadLineAsync()而不是ReadToEndAsync()来执行此操作,文本框将更新得更频繁,因此更容易看到
我在教程中搜索了异步操作,但都显示了基于文件的操作。查看Microsoft参考资料中的重定向标准输出和异步更让我困惑
有人能帮我想出一种用ReadLineAsync()逐行读取输出的方法吗?明白了。下面是有效的代码。这样,文本框将逐行更新,死区不再出现
public async void Button1_Click(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(Input_job_file) && !String.IsNullOrEmpty(Log_file ))
{
textBox3.Text = "Executing code...\r\n";
string outtext;
string batpath = string.Format(Settings.Default.path_to_gamess + "\\rungms.bat");
string arguments = string.Format("{0} {1} {2} 0 {3}", Input_job_file, Settings.Default.version, Ncpus, Log_file);
//Here we need to copy the input file to the gamess directory in order to avoid errors
File.Copy(Input_job_file, Settings.Default.path_to_gamess + "\\" + FileNameWithoutExtension + ".inp");
Process gamessjob = new Process();
gamessjob.StartInfo.ErrorDialog = true;
gamessjob.StartInfo.UseShellExecute = false;
gamessjob.StartInfo.CreateNoWindow = true;
gamessjob.StartInfo.RedirectStandardOutput = true;
gamessjob.StartInfo.RedirectStandardError = true;
gamessjob.EnableRaisingEvents = true;
gamessjob.StartInfo.WorkingDirectory = Settings.Default.path_to_gamess;
gamessjob.StartInfo.FileName = batpath;
gamessjob.StartInfo.Arguments = arguments; //input_file, version, number_of_processors, "0", output_name]
gamessjob.Start();
//STDOUT redirection to our textbox in readonly mode
while((outtext = await gamessjob.StandardOutput.ReadLineAsync()) != null)
{
textBox3.Text += outtext + "\r\n";
}
textBox3.Text += "\r\nProcess executed!";
//here we clean up some stuff after GAMESS code ends
File.Delete(Settings.Default.path_to_gamess + "\\" + FileNameWithoutExtension + ".inp");
MessageBox.Show("Code executed!\nCheck output for errors or messages.", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else MessageBox.Show("Input file and/or output location is invalid!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
明白了。下面是有效的代码。这样,文本框将逐行更新,死区不再出现
public async void Button1_Click(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(Input_job_file) && !String.IsNullOrEmpty(Log_file ))
{
textBox3.Text = "Executing code...\r\n";
string outtext;
string batpath = string.Format(Settings.Default.path_to_gamess + "\\rungms.bat");
string arguments = string.Format("{0} {1} {2} 0 {3}", Input_job_file, Settings.Default.version, Ncpus, Log_file);
//Here we need to copy the input file to the gamess directory in order to avoid errors
File.Copy(Input_job_file, Settings.Default.path_to_gamess + "\\" + FileNameWithoutExtension + ".inp");
Process gamessjob = new Process();
gamessjob.StartInfo.ErrorDialog = true;
gamessjob.StartInfo.UseShellExecute = false;
gamessjob.StartInfo.CreateNoWindow = true;
gamessjob.StartInfo.RedirectStandardOutput = true;
gamessjob.StartInfo.RedirectStandardError = true;
gamessjob.EnableRaisingEvents = true;
gamessjob.StartInfo.WorkingDirectory = Settings.Default.path_to_gamess;
gamessjob.StartInfo.FileName = batpath;
gamessjob.StartInfo.Arguments = arguments; //input_file, version, number_of_processors, "0", output_name]
gamessjob.Start();
//STDOUT redirection to our textbox in readonly mode
while((outtext = await gamessjob.StandardOutput.ReadLineAsync()) != null)
{
textBox3.Text += outtext + "\r\n";
}
textBox3.Text += "\r\nProcess executed!";
//here we clean up some stuff after GAMESS code ends
File.Delete(Settings.Default.path_to_gamess + "\\" + FileNameWithoutExtension + ".inp");
MessageBox.Show("Code executed!\nCheck output for errors or messages.", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else MessageBox.Show("Input file and/or output location is invalid!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
你处理的文件有多大,真的有那么大以至于导致死锁吗?如果是这样的话,我会认为复制完整文件并删除它会带来很多麻烦,因为您没有进行异步操作。还有,你在用这个文件做什么?你需要整件东西还是一行一行地使用。我甚至没有看到你在使用文件的结果,你在用它做什么?实际上,我需要的是一种方法,来获取终端在执行这个bat文件时显示的文本。所有与文件生成和内容相关的事情都是由bat文件本身内部的代码处理的。终端输出的长度不在我的直接控制之下。这是因为根据特定的分子模型文件iI给出的脚本,输出可能会有所不同。有时是140行打印在终端上,而另一些时候是100k+行。我不知道你在用文件做什么,异步在执行非cpu密集型操作(例如网络请求/IO等)时释放线程。你说“文本框将更新得更频繁,因此更”令人愉快“。您这样说是什么意思?是否正在更新文件中每行的文本框?或者您需要更新渲染(例如gui被锁定的时间)吗?我认为您想要的是仍然使用ReadAsync,但是您想要创建一个较小的缓冲区大小,这样您就不会一次读取整个文件。e、 g创建for循环并读入块,而不是执行
0,(int)SourceStream.Length
您执行的lastBuffer,bufferSize
您处理的文件有多大,它真的有那么大导致死锁吗?如果是这样的话,我会认为复制完整文件并删除它会带来很多麻烦,因为您没有进行异步操作。还有,你在用这个文件做什么?你需要整件东西还是一行一行地使用。我甚至没有看到你在使用文件的结果,你在用它做什么?实际上,我需要的是一种方法,来获取终端在执行这个bat文件时显示的文本。所有与文件生成和内容相关的事情都是由bat文件本身内部的代码处理的。终端输出的长度不在我的直接控制之下。这是因为根据特定的分子模型文件iI给出的脚本,输出可能会有所不同。有时是140行打印在终端上,而另一些时候是100k+行。我不知道你在用文件做什么,异步在执行非cpu密集型操作(例如网络请求/IO等)时释放线程。你说“文本框将更新得更频繁,因此更”令人愉快“。您这样说是什么意思?是否正在更新文件中每行的文本框?或者您需要更新渲染(例如gui被锁定的时间)吗?我认为您想要的是仍然使用ReadAsync,但是您想要创建一个较小的缓冲区大小,这样您就不会一次读取整个文件。e、 g创建for循环并读入块,而不是执行0,(int)SourceStream.Length
您执行的lastBuffer,bufferSize
我添加了以下内容以防止出现这种情况:button1.Enabled=false代码>然后是它所遵循的执行部分button1.Enabled=true代码>我添加了以下内容以防止出现这种情况:button1.Enabled=false代码>然后是它所遵循的执行部分button1.Enabled=true代码>