我有一个带有三个控制台的项目。一个控制台将同时打开另外两个进程,以独立完成某些工作。
所有控制台都使用dotnet核心框架。
MultipleConsoleWindows
是主要应用程序,如下所示:
static void Main(string[] args)
{
Task t1 = new Task(async () => { await ProcessManager.StartAsync("c1"); });
Task t2 = new Task(async () => { await ProcessManager.StartAsync("c2"); });
// what should do here ?
Console.WriteLine("done");
Console.Read();
}
和ProcessManager类:
public static class ProcessManager
{
const string C1 = @"pathTo\ConsoleNumberOne.dll";
const string C2 = @"pathTo\ConsoleNumberTwo.dll";
public static async Task<string> StartAsync(string type)
{
Console.WriteLine($"Start {type}");
var proc = type.Equals("c1") ? C1 : C2;
return await Task.Run(() => StartProcess(proc));
}
static string StartProcess(string proc)
{
ProcessStartInfo procStartInfo = new ProcessStartInfo();
procStartInfo.FileName = "dotnet";
procStartInfo.Arguments = $"\"{proc}\"";
procStartInfo.WorkingDirectory = Path.GetDirectoryName(proc);
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardError = true;
int output = 0;
StringBuilder sb = new StringBuilder();
using (Process pr = new Process())
{
pr.StartInfo = procStartInfo;
pr.OutputDataReceived += (s, ev) =>
{
if (string.IsNullOrWhiteSpace(ev.Data))
{
return;
}
sb.AppendLine(ev.Data);
string[] split = ev.Data.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
int.TryParse(split[split.Length - 1], out output);
};
pr.ErrorDataReceived += (s, err) =>
{
if (!string.IsNullOrWhiteSpace(err.Data))
{
sb.AppendLine(err.Data);
output = 0;
}
};
pr.EnableRaisingEvents = true;
pr.Start();
pr.BeginOutputReadLine();
pr.BeginErrorReadLine();
pr.WaitForExit();
return sb.ToString();
}
}
}
ConsoleNumberOne
和ConsoleNumberTwo
看起来很相似
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Thread.Sleep(10000);
}
和
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Thread.Sleep(5000);
}
我正在尝试同时打开两个自己工作的控制台。
如何在MultipleConsoleWindows
端实现这一目标?
答案 0 :(得分:0)
Maebe这可以解决问题
static void Main(string[] args)
{
Action t1 = new Action(async () => { await ProcessManager.StartAsync("c1"); });
Action t2 = new Action(async () => { await ProcessManager.StartAsync("c2"); });
Parallel.Invoke(t1, t2);
Console.WriteLine("done");
Console.Read();
}
答案 1 :(得分:0)
无需使用Task.Run
来启动子进程。 documentation suggests并不是使用WaitForExit();
来阻塞直到进程退出,而是:
为避免阻塞当前线程,请使用Exited事件。
在执行任务之前,事件是异步执行作业和接收通知的方法之一。这称为Event-Based Asynchronous Pattern
。可以使用TaskCompletionSource将事件转换为Tasks。 How to: Wrap EAP Patterns in a Task中对此进行了描述。
该示例比应有的更为冗长。在这种情况下,将Exited
转换为Task很简单:
static Task<string> StartProcess(string proc)
{
StringBuilder sb = new StringBuilder();
Process pr = new Process
{
StartInfo = procStartInfo
};
var tcs = new TaskCompletionSource<string>();
pr.Exited += (o, e) =>
{
tcs.SetResult(sb.ToString());
pr.Dispose();
};
....
return tcs.Task;
}
可以通过这种方式启动并等待多个进程:
static async Task Main(string[] args)
{
var p1 = StartProcess("--version");
var p2 = StartProcess("--list-runtimes");
string[] responses=await Task.WhenAll(p1, p2);
...
}
TaskCompletionSource.SetResult完成tcs返回的任务并设置其结果,在这种情况下为字符串。 SetException可用于将任务设置为故障状态,等待时引发异常。例如,这可以用于取消任何进程返回非零退出代码
的等待