C# 如何使用带有C的线程启动进程#

C# 如何使用带有C的线程启动进程#,c#,multithreading,C#,Multithreading,问题:线程在启动引发异常的进程时被放弃 编辑 我正在重复这个问题以及我试图实现的目标,如何从线程开始一个进程 背景故事 我需要进程来运行exe的,如imagemagick,libreoffice。我正在尝试转换许多文件,然后将其结果附加到一个文件中。稍后将对状态文件进行进一步处理 我不擅长线程,我一直在引用一些关于stackoverflow的帖子,比如 我正在尝试这样做: foreach (Preset pr in listOfPreset) { Conv

问题:线程在启动引发异常的进程时被放弃

编辑

我正在重复这个问题以及我试图实现的目标,如何从线程开始一个进程

背景故事

我需要进程来运行exe的,如imagemagick,libreoffice。我正在尝试转换许多文件,然后将其结果附加到一个文件中。稍后将对状态文件进行进一步处理

我不擅长线程,我一直在引用一些关于stackoverflow的帖子,比如

我正在尝试这样做:

foreach (Preset pr in listOfPreset)
        {
            ConvertRipper cRipper = new ConvertRipper(pr);
            ThreadStart job = (new ThreadStart(()=> cRipper.Ripper()));
            Thread th = new Thread(job);
            th.Start();
        }


public void Ripper()
    {
        //create the folders before converting
        if (!Directory.Exists(preset.OutputFilePath))
            Directory.CreateDirectory(preset.OutputFilePath);

        Document document = CreateDocument(preset);
        ProcessResult pr = v3Engine.Convert(Const.IMAGEMAGICK, v3Engine.ConvertImages(document));

        if (pr.ExitCode == 0)
        {
            //write status to a file the xml status
        }
    }
现在,在Ripper方法中的某个地方,我确实有一个开始的过程,我基本上调用一个windows exe来转换一些文件

转换方法

    Process proc = new Process();
    proc.StartInfo.RedirectStandardOutput = true;
    proc.StartInfo.RedirectStandardError = true;
    proc.StartInfo.UseShellExecute = false;

    proc.StartInfo.Arguments = arguments;
    proc.StartInfo.CreateNoWindow = true;
    proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

    proc.ErrorDataReceived += (sender, args) => error.Append(args.Data);
    proc.OutputDataReceived += (sender, args) => output.Append(args.Data);

    proc.Start(); *loc1*

    proc.BeginOutputReadLine();
    proc.BeginErrorReadLine();
    proc.WaitForExit();

    ProcessResult pr = new ProcessResult
    {
          StandardOutput = output.ToString(),
          ErrorOutput = error.ToString(),
          ExitCode = proc.ExitCode
    };

     proc.Close();

     return pr;`
编辑

堆栈跟踪 “在System.Diagnostics.ProcessStartInfo.set\u重定向标准错误(布尔值 值)\r\n位于Conversion.Converter.AbstractConvertor.Convert(字符串 执行器,字符串参数)中 C:\Users\dev\source\repos\conversation\conversation\Converter\AbstractConvertor.cs:line 54“

*例外状态:**

无法计算表达式,因为代码已优化或 本机框架位于调用堆栈的顶部

在我的过程完成之后。我想将进程状态写入文件

事实上,我不明白它如何适合我的情况。因为,我已经在一个方法裂土器上使用了它,该裂土器在Convert方法中间接包含Process.start()

附言:我读过这样的评论:“当然,一个进程是在一个新进程中启动的(一组完全独立的线程),所以在一个线程上启动它是完全没有意义的。”

@Downvoter,你能分享一下我如何根据你的喜好改变这个问题吗

我觉得我的剧本需要它。如果没有,你能建议我还能做些什么吗


任何提示都将不胜感激。如果您对我的测试有任何疑问,请告诉我

您应该使用任何序列化程序(例如Json)序列化结果,然后将其写入文件:

if (pr.ExitCode == 0)
{
   string json = JsonConvert.SerializeObject(pr);
   File.WriteAllText("FileWithResult.txt", json);
}
如果有多个线程,则应使用不同的输出文件名。
或者,您可以使用任何记录器,例如NLog。

一个快速而肮脏的解决方案:等待所有线程完成:

var threads = new List<Thread>();
foreach (Preset pr in listOfPreset)
{
   ConvertRipper cRipper = new ConvertRipper(pr);
   ThreadStart job = (new ThreadStart(()=> cRipper.Ripper()));
   Thread th = new Thread(job);
   th.Start();
   threads.Add(th);
}

foreach (t in threads) t.Join();
var threads=newlist();
foreach(Preset列表中的预设pr)
{
ConvertRipper cRipper=新ConvertRipper(pr);
ThreadStart作业=(新ThreadStart(()=>cRipper.Ripper());
线程th=新线程(作业);
th.Start();
添加(th);
}
foreach(t in threads)t.Join();
这样线程就不会被丢弃


更优雅的解决方案包括启动进程但不启动任何线程的
任务
,它可以使用
任务完成源代码
进程来实现。退出
事件

对您试图实现的目标使用线程没有意义,因为进程有自己的线程,因此,你试图在等式中引入线程,这让你自己的生活变得复杂

从本质上讲,您试图做的事情看起来像是线程,因为您希望东西异步运行,并等待它们的结果,然后再继续在程序中运行

为此,可以使用线程,让每个线程启动一个进程,然后让这些线程阻塞,直到进程退出,并将结果写入结果文件,让主线程等待所有其他线程完成,然后再继续程序的其余部分

Task
是一个很好的方法来完成这项任务,因为它为您隐藏了线程的所有不好之处(在大多数情况下)

您可以创建一个
任务
数组,并为启动的每个流程创建一个
任务
。在该任务中,您希望以
process.WaitForExit()
结束该任务,它将阻止该任务,直到进程完成,然后您可以将进程结果写入该文件。使用数组跟踪您完成的所有
任务

在创建和启动任务的循环之后,可以使用
Task.WaitAll()
等待所有任务完成并继续执行程序的其余部分,因为这将阻塞主线程,直到每个任务完成

大概是这样的:

int processCount = 5; // or whatever
Task[] tasks = new Task[processCount];
for(int i = 0; i < processCount; i++)
{
  Task t = new Task(() => {
    ProcessStartInfo psi = ...
    // start your process here
    process.WaitForExit();
    if (pr.ExitCode == 0)
    {
        //write status to a file the xml status
        // remember to synchronize writes to the file so multiple tasks
        // don't try to open the same file at the same time
        // or just use different file names for each process
    }
  });
  t.Start();
  tasks[i] = t;
}

Task.WaitAll(tasks);
// continue with rest of program

这是一个优雅而酷的解决方案:使用任务而不旋转线程

请恕我直言,这有点乏味,我删除了所有与理解该想法无关的代码:

首先,我们有以下扩展类,它是创建任务的关键,这些任务执行流程时不会使线程旋转:

public static class Extensions
{
    public static Task ExecuteAsync(this Process process)
    {
        var tcs = new TaskCompletionSource<bool>();
        process.Exited += (sender, eventArgs) =>
        {
            tcs.TrySetResult(true);
        };
        try
        {
            process.Start();
        }
        catch (Exception ex)
        {
            tcs.TrySetException(ex);
        }
        return tcs.Task;
    }
}
裂土器
也是
异步的

static async Task Ripper()
{
    // prepare convert arguments
   ProcessResult result = await Convert();
   // do something with the result
}
最后是主要方法:

var tasks = new List<Task>();
foreach (var item in list)
{
    tasks.Add(Ripper());
}
Task.WaitAll(tasks.ToArray());
var tasks=newlist();
foreach(列表中的变量项)
{
添加(裂土器());
}
Task.WaitAll(tasks.ToArray());

希望有帮助。

是的,您可能希望构造一个任务,该任务的完成由进程发出信号。已退出事件。如果Process类有一个WaitForExitAsync方法那就太好了,但是它不清楚你在问什么,你的帖子没有任何问题。你能显示抛出的异常吗?。请编辑您的问题并澄清。@JesúsLópez;我必须重新启动我的VS,我将很快共享异常信息。@BinoyCherian。请向ex.ToString()显示您在此处并不真正需要线程,因为您正在启动的进程已经在它们自己的线程上。这就是过程的工作原理。是否要等待所有进程完成后再写出状态?是否要在每个进程完成后立即写入进程的状态?这与问题无关,问题是关于使用线程启动进程。它不询问如何写入输出。@Logan。这个问题还不清楚,我还以为他在问如何解决这个问题
static async Task Ripper()
{
    // prepare convert arguments
   ProcessResult result = await Convert();
   // do something with the result
}
var tasks = new List<Task>();
foreach (var item in list)
{
    tasks.Add(Ripper());
}
Task.WaitAll(tasks.ToArray());