场景:前端请求后端接口,并发送一段c语言代码,由后端调用命令行执行gcc编译,然后再调编译后生成的exe程序读出exe程序的输出结果,接口把这个结果返回给前端页面。问题:在队列阻塞情况下执行一次一条没问题,都能得到正确结果,但是并发请求时就会出现部分请求失败,无法返回结果问题。备注:代码设置了cmd命令行超时关闭并杀死进程。
 积分不多,请大家多多包涵
代码: public async static Task<CompilerData> RunCmd(CompilerData cdata)
        {
            await Task.Run(() =>
             {
                 var cancellationTokenSource = new CancellationTokenSource();
                 var th = new Thread(() =>
                 {
                     Run(cdata);
                 });
                 cancellationTokenSource.Token.Register(() =>
                 {
                     th.Abort();
                     cdata.TimeOut = true;
                     Process[] process = Process.GetProcesses();
                     var proc = process.FirstOrDefault(i => i.ProcessName == cdata.ExeName);
                     if (proc != null)
                     {
                         if (!proc.HasExited)
                         {
                             //立即停止相关进程。意即,进程没回应,强制关闭
                             proc.Kill();
                         }
                         if (proc != null)
                         {
                             proc.Close();
                             proc.Dispose();
                             proc = null;
                         }
                     }
                 });
                 cancellationTokenSource.CancelAfter(60000);
                 th.Start();
                 th.Join();             });
            return cdata;
        } /// <summary>
        /// 执行命令
        /// </summary>
        /// <param name="cdata"></param>
        private static void Run(CompilerData cdata)
        {
            List<ProcessModel> proclist = HelperCache.GetCache("Process") as List<ProcessModel>;
            if (proclist == null)
            {
                proclist = new List<ProcessModel>();
            }
            cdata.IsSuccess = false;
            cdata.Error = "命令为空";
            if (!string.IsNullOrWhiteSpace(cdata.ExecuteThis))
            {
                Process proc = new Process();//创建进程对象  
                proc.StartInfo.FileName = CMDPath;//设定需要执行的命令  
                proc.StartInfo.Arguments = "/C " + cdata.Executor + " " + cdata.ExecuteThis;//“/C”表示执行完命令后马上退出  
                proc.StartInfo.UseShellExecute = false;//不使用系统外壳程序启动 
                proc.StartInfo.RedirectStandardInput = false;//不重定向输入  
                proc.StartInfo.RedirectStandardOutput = true; //重定向输出  
                proc.StartInfo.RedirectStandardError = true;
                proc.StartInfo.CreateNoWindow = true;//不创建窗口  
                try
                {
                    if (proc.Start())
                    {
                        
                        Stopwatch watch = new Stopwatch();
                        watch.Start();
                        ProcessModel model = new ProcessModel()
                        {
                            Id = proc.Id,
                            ProcessName = proc.ProcessName,
                            StartTime = proc.StartTime,
                            MaxCompileTime = cdata.MaxCompileTime
                        };
                        proclist.Add(model);
                        HelperCache.SetCache("Process", proclist);
                        HelperCache.RemoveAllCache(cdata.ExeName);
                        HelperCache.SetCache(cdata.ExeName, cdata.ExeName);
                        cdata.Success = proc.StandardOutput.ReadToEnd();//读取进程的输出 
                        cdata.Error = proc.StandardError.ReadToEnd();
                        watch.Stop();
                        proc.WaitForExit();
                        cdata.CompileTimeMs = watch.Elapsed.TotalMilliseconds;
                        OnlineData.Helper.HelperLog.Info(string.Format("成功{0},失败{1}", cdata.Success, cdata.Error));
                        cdata.IsSuccess = string.IsNullOrWhiteSpace(cdata.Error) ? true : false;
                    }
                }
                catch (Exception ex)
                {
                    cdata.Error = proc.StandardError.ReadToEnd();
                    OnlineData.Helper.HelperLog.Info(string.Format("成功{0},失败{1}", cdata.Success, cdata.Error), ex);
                    cdata.IsSuccess = false;
                }
                finally
                {
                    if (!proc.HasExited)
                    {
                        //立即停止相关进程。意即,进程没回应,强制关闭
                        proc.Kill();
                    }
                    if (proc != null)
                    {
                        proc.Close();
                        proc.Dispose();
                        proc = null;
                    }
                }
            }
        }

解决方案 »

  1.   

    是你的HelperCache的问题吗,可能不是线程安全的?
    理论上起很多控制台窗口运行gcc是不会有问题
    你可以试下2任务并行,看一下是不是cpu负担重从而导致任务超时
      

  2.   

    用 Task 你还用什么 Thread?
      

  3.   


    HelperCache只是用来记录一下缓存,在另一个地方循环读取出记录杀死进程,防止一些假死进程占用资源。
    另外我看生成的文件目录中的的文件都正常,请求多少100次生成100个文件,而且文件都没有出错。
    5个任务同时执行不会出错,都正常返回。
    我不知道是不是我用的工具有响应时间设置,一旦响应时间超时就认为失败?
    我用的 http://coolaf.com/tool/testing 这个工具本地化测试的。1000次请求队列访问是不会出问题的,调用cmd是需要时间的,同时都过来调用cpu在负载过重情况下会阻塞其他请求,是不?
      

  4.   

    是不是由于执行的是同一个exe,
    第一个还没完成,第二个要生成exe时,就出问题了
      

  5.   


    您好,首先不能死等,因为如果前端传过来一个死循环的代码,后端编译执行岂不是死在这里了,所以要超时杀死他们。其次,因为水平问题,用task我不会设置超时终止进程,所以用了Thread。这里调用的等于是第三方程序了无法再其内部设置终止判断。最后这里每次前端传过来的代码都会不同,因为这是考试题,同一道编程题,会有不同的写法,所以后端编译的也是唯一的一个exe程序。在这个情况下,如果在100人的考场,假设一个极端的情景:同一时刻50个请求过来就会有部分请求失败的情况出现。
      

  6.   


    生成exe没问题,就是返回结果有些超时严重
      

  7.   


    另外问一下大神,在Task.Run中用thread会有什么结果?因为这个外部是一个异步的方法,调用这个RunCmd,所以需要有await,用thread无法使用await,那么在外部的方法中就要再写一个Task.Run来包裹RunCmd了,这样的话和在RunCmd里面使用Task.Run岂不是一样了?