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

本文关键字:Factory Task 失败 调用 Parallel ForEach 线程 | 更新日期: 2023-09-27 18:33:03

首先,对不起,代码部分很长,我简化了它并尝试创建一个测试类,但它仍然很长,再次抱歉...

我正在努力处理一些多线程代码,感谢您的帮助我将并行类与 ForEach 循环一起使用,该循环调用一些方法,但有时(并非总是!(会失败。

这是我的代码:

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


}

这里的问题是,有时我会收到此错误(我可以在事件日志中看到(:

执行错误发生进程:

无法访问已释放的对象。对象名称:"进程"。Stacktrace: at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo(   在 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 的帮助和支持中心。

我不知道这里出了什么问题,希望你能给我一些建议。 多谢。

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

基于Mayer的问题,我想知道我是否真的需要任务工厂,我意识到我不需要,所以我替换了方法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;
    }