7

我有一些 System.Diagnotics.Processes 要运行。我想自动调用它们的 close 方法。显然“使用”关键字为我做了这个。

这是使用 using 关键字的方式吗?

foreach(string command in S) // command is something like "c:\a.exe"
{
    try
    {
        using(p = Process.Start(command))
        {
            // I literally put nothing in here.
        }
    }
    catch (Exception e)
    {
        // notify of process failure
    }
}

我想启动多个进程同时运行。

4

3 回答 3

15
using(p = Process.Start(command))

这将编译,因为Process类实现了IDisposable,但是您实际上想要调用该Close方法。
从逻辑上讲,该Dispose方法会Close为您调用,并且通过使用反射器深入研究 CLR,我们可以看到它实际上为我们执行了此操作。到现在为止还挺好。

再次使用反射器,我查看了该Close方法的作用——它释放了底层的本机 win32 进程句柄,并清除了一些成员变量。这(释放外部资源)正是IDisposable 模式应该做的。

但是我不确定这是否是您想要在这里实现的目标。

释放底层句柄只是对 Windows 说“我不再对跟踪这个其他进程感兴趣”。它实际上不会导致其他进程退出,或导致您的进程等待。

如果要强制它们退出,则需要p.Kill()在进程上使用该方法-但是请注意,杀死进程绝不是一个好主意,因为它们无法自行清理,并且可能会留下损坏的文件,并且很快。

如果您想等待他们自行退出,您可以使用p.WaitForExit()- 但是这仅在您一次等待一个进程时才有效。如果你想同时等待它们,那就很棘手了。

通常你会使用WaitHandle.WaitAll这个,但由于没有办法从 a 中WaitHandle取出一个对象System.Diagnostics.Process,你不能这样做(说真的,微软在想什么?)。

您可以为每个进程启动一个线程,并在这些线程中调用 `WaitForExit,但这也是错误的方法。

相反,您必须使用 p/invoke 来访问本机 win32WaitForMultipleObjects功能。

这是一个示例(我已经测试过,并且确实有效)

[System.Runtime.InteropServices.DllImport( "kernel32.dll" )]
static extern uint WaitForMultipleObjects( uint nCount, IntPtr[] lpHandles, bool bWaitAll, uint dwMilliseconds );

static void Main( string[] args )
{
    var procs = new Process[] {
        Process.Start( @"C:\Program Files\ruby\bin\ruby.exe", "-e 'sleep 2'" ),
        Process.Start( @"C:\Program Files\ruby\bin\ruby.exe", "-e 'sleep 3'" ),
        Process.Start( @"C:\Program Files\ruby\bin\ruby.exe", "-e 'sleep 4'" ) };
    // all started asynchronously in the background

    var handles = procs.Select( p => p.Handle ).ToArray();
    WaitForMultipleObjects( (uint)handles.Length, handles, true, uint.MaxValue ); // uint.maxvalue waits forever

}
于 2008-10-09T00:12:28.610 回答
3

供参考:IDisposable对象的using关键字:

using(Writer writer = new Writer())
{
    writer.Write("Hello");
}

只是编译器语法。它编译成的是:

Writer writer = null;
try
{
    writer = new Writer();
    writer.Write("Hello");
}
finally
{
    if( writer != null)
    {
        ((IDisposable)writer).Dispose();
    }
}

using更好一点,因为编译器会阻止您在 using 块内重新分配编写器引用。

框架指南第 9.3.1 节 p。256 状态:

考虑提供方法 Close(),除了 Dispose(),如果 close 是该领域的标准术语。


在您的代码示例中,外部 try-catch 是不必要的(见上文)。

由于 Dispose() 一旦p超出范围就会被调用,因此使用可能不会做你想做的事情。这不会关闭进程(已测试)。

进程是独立的,因此除非您将p.WaitForExit()它们称为分拆并完全独立于您的程序做自己的事情。

与直觉相反,对于 Process,Close() 仅释放资源但使程序运行。CloseMainWindow() 可以对某些进程起作用,而 Kill() 可以杀死任何进程。CloseMainWindow() 和 Kill() 都可以抛出异常,所以在 finally 块中使用它们时要小心。

最后,这里有一些代码等待进程完成,但在发生异常时不会终止进程。我并不是说它比 Orion Edwards 更好,只是不同而已。

List<System.Diagnostics.Process> processList = new List<System.Diagnostics.Process>();

try
{
    foreach (string command in Commands)
    {
        processList.Add(System.Diagnostics.Process.Start(command));
    }

    // loop until all spawned processes Exit normally.
    while (processList.Any())
    {
        System.Threading.Thread.Sleep(1000); // wait and see.
        List<System.Diagnostics.Process> finished = (from o in processList
                                                     where o.HasExited
                                                     select o).ToList();

        processList = processList.Except(finished).ToList();
        foreach (var p in finished)
        {
            // could inspect exit code and exit time.
            // note many properties are unavailable after process exits
            p.Close();
        }
    }
}
catch (Exception ex)
{
    // log the exception
    throw;
}
finally
{
    foreach (var p in processList)
    {
        if (p != null)
        {
            //if (!p.HasExited)
            // processes will still be running
            // but CloseMainWindow() or Kill() can throw exceptions
            p.Dispose();
        }

    }
}

我没有打扰 Kill()'ing 进程,因为代码开始变得更加丑陋。阅读msdn文档以获取更多信息。

于 2008-10-09T01:50:13.750 回答
0
try
{
   foreach(string command in S) // command is something like "c:\a.exe"
   {
      using(p = Process.Start(command))
      {
        // I literally put nothing in here.
      }

    } 
}
catch (Exception e)
{
    // notify of process failure
}

它起作用的原因是因为当异常发生时,变量 p 超出范围,因此调用 Dispose 方法来关闭进程是这样​​的。此外,我认为您希望为每个命令关闭一个线程,而不是等待可执行文件完成后再继续执行下一个命令。

于 2008-10-08T23:58:49.030 回答