首先,对于很长的代码部分感到抱歉,我简化了它并试图创建一个测试类,但它仍然很长,再次抱歉......
我正在努力学习一些多线程代码,感谢您的帮助 我使用带有ForEach循环的Parallel Class调用一些方法,但有时(并非总是!)失败。
这里是我的代码:
public class SomeInstallClass
{
public void main()
{
List<string> list = new List<string>();
list.Add("someExe1.exe");
list.Add("someExe2.exe");
list.Add("someExe3.exe");
list.Add("someExe4.exe");
parallelInstallation(list);
}
private System.Threading.CancellationTokenSource cts = new System.Threading.CancellationTokenSource();
private void parallelInstallation(List<string> exeStrings)
{
var po = new ParallelOptions();
po.CancellationToken = cts.Token;
try
{
Parallel.ForEach(exeStrings, po, t => { executeProcessSO(t); });
}
catch (Exception e)
{
Console.WriteLine("catch of the Parallel Executions: " + e.Message);
}
Console.WriteLine("All threads complete");
}
private ProcessStartInfo getProcessStartInfoSO(string someExe)
{
//Process myProcess = new Process();
//string fullExe = string.Concat("\"", installUtilFile, "\"");
ProcessStartInfo myProcessStartInfo = new ProcessStartInfo(someExe, "");
myProcessStartInfo.WorkingDirectory = "someWorkingDirectory";
myProcessStartInfo.UseShellExecute = false;//no standard input / output / error if true
myProcessStartInfo.RedirectStandardError = true;
myProcessStartInfo.RedirectStandardOutput = true;
myProcessStartInfo.CreateNoWindow = true;
myProcessStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
return myProcessStartInfo;
}
public void executeProcessSO(string someExe)
{
string standardOutput = string.Empty;
string standardError = string.Empty;
string exit = string.Empty;
ProcessStartInfo myProcessStartInfo = getProcessStartInfoSO(someExe);
exit = executeProcessSO(myProcessStartInfo, out standardOutput, out standardError);
Console.WriteLine("Process completed");
}
private string executeProcessSO(ProcessStartInfo myProcessStartInfo, out string standardOutput, out string standardError)
{
TaskScheduler.UnobservedTaskException += (sender, args) =>
{
string err = string.Empty;
foreach (var ex in args.Exception.InnerExceptions)
{
//Log(ex);
err = err + "\n" + ex.Message;
err = err + "\nStacktrace:" + ex.StackTrace; ;
if (ex.InnerException != null)
{
err = err + "\n" + ex.InnerException.Message;
err = err + "\nStacktrace" + ex.InnerException.StackTrace;
}
}
EventLog.WriteEntry("InstallationService", "Error in executeProcess occured: \n" + err, EventLogEntryType.Error);
args.SetObserved();
};
int exitCode;
using (var process = new Process())
{
process.StartInfo = myProcessStartInfo;
using (Task taskWaiter = Task.Factory.StartNew(() => process.Start()))
{
try
{
//1. run main task in specified time
taskWaiter.Wait(10000);
//2. if is not completed by now, kill it
if (!(taskWaiter.IsCompleted))
{
if (!process.HasExited)
{
try
{
process.Kill();
process.WaitForExit(10000); //just to be sure...
standardError = "Timeout occured";
standardOutput = string.Empty;
EventLog.WriteEntry("InstallationService", standardError);
}
catch (Exception e)
{
standardError = "Timeout occured and exception occured during the kill operation. Ex: " + e.Message;
standardOutput = string.Empty;
throw e;
}
}
}
}
catch (Exception oe)
{
standardError = "Exception in the taskWaiter occured. Ex: " + oe.Message;
throw oe;
}
//this point should be reached only if the process didn't timed out --> in this case read the output and error
//using (Task<bool> processWaiter = Task.Factory.StartNew(() => process.WaitForExit(timeOutInMilliseconds)))
using (Task<string> outputReader = Task.Factory.StartNew((Func<object, string>)ReadStream, process.StandardOutput))
using (Task<string> errorReader = Task.Factory.StartNew((Func<object, string>)ReadStream, process.StandardError))
{
//3. wait for the output reader and error reader to complete (if not already done)
Task.WaitAll(taskWaiter, outputReader, errorReader);
standardError = errorReader.Result;
standardOutput = outputReader.Result;
try
{
process.WaitForExit(10000); //just to be sure...
exitCode = process.ExitCode;
}
catch (Exception e2)
{
EventLog.WriteEntry("InstallationService", "Error in process.WaitForExit occured: \n" + e2.Message, EventLogEntryType.Error);
throw e2;
}
}
}
}
if (exitCode.Equals(0))
return "Success";
else
return "Failed";
}
private string ReadStream(object streamReader)
{
string result = ((StreamReader)streamReader).ReadToEnd();
return result;
}
}
问题在于,有时我会收到此错误(我可以在EventLog中看到):
执行executeProcess时出错:
Cannot access a disposed object. Object name: 'Process'. Stacktrace: at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo) at ProcessHelper.Execution.c__DisplayClass7.b__4() at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj) at System.Threading.Tasks.Task.Execute()
有关详细信息,请参阅http://go.microsoft.com/fwlink/events.asp上的“帮助和支持中心”。
我不知道这里有什么问题,希望你能给我一些建议。非常感谢。
答案 0 :(得分:0)
基于theMayer的问题,我想知道我是否真的需要任务工厂而且我意识到我没有,所以我替换了方法executeProcessSO,现在它可以工作。
private string executeProcessSO(ProcessStartInfo myProcessStartInfo, out string standardOutput, out string standardError)
{
string rslt = "Failed";
int exitCode = -1;
using (var process = new Process())
{
process.StartInfo = myProcessStartInfo;
process.Start();
using (Task<string> outputReader = Task.Factory.StartNew((Func<object, string>)ReadStream, process.StandardOutput))
using (Task<string> errorReader = Task.Factory.StartNew((Func<object, string>)ReadStream, process.StandardError))
{
process.WaitForExit(10000);
standardError = errorReader.Result;
standardOutput = outputReader.Result;
try
{
if (process.HasExited)
{
exitCode = process.ExitCode;
if (exitCode.Equals(0))
rslt = "Success";
}
else
{
process.Kill();
rslt = "Timeout";
}
}
catch (Exception e2)
{
EventLog.WriteEntry("InstallationService", "Error in process.WaitForExit occured: \n" + e2.Message.ToString(), EventLogEntryType.Error);
throw e2;
}
}
}
return rslt;
}