有时使用Parallel.ForEach调用的Task.Factory线程失败

时间:2014-12-03 17:15:01

标签: c# .net multithreading

首先,对于很长的代码部分感到抱歉,我简化了它并试图创建一个测试类,但它仍然很长,再次抱歉......

我正在努力学习一些多线程代码,感谢您的帮助 我使用带有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上的“帮助和支持中心”。

我不知道这里有什么问题,希望你能给我一些建议。非常感谢。

1 个答案:

答案 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;
    }