如何以编程方式在C#中使用“using”关键字?

问题描述:

我有一些System.Diagnotics.Processes运行。我想自动调用它们的close方法。显然,“使用”关键字为我做了这个。如何以编程方式在C#中使用“using”关键字?

这是使用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 
    } 
} 

我想启动多个进程并发运行。

using(p = Process.Start(command)) 

这将编译,因为Process类实现IDisposable,但是你真的想调用Close方法。
逻辑会认为Dispose方法会为你调用Close,通过使用反射器挖掘CLR,我们可以看到它实际上是为我们做的。到现在为止还挺好。

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

但是我不确定这是你想在这里实现的。

发布底层句柄只是对windows说'我不再有兴趣跟踪这个其他进程'。在任何时候它都不会导致其他进程退出,或导致进程等待。

如果你想强制它们退出,你需要在进程上使用p.Kill()方法 - 但是应该建议,杀死进程并不是一个好主意,因为它们不能自行清理,并且可能会离开在损坏的文件后面,等等。

如果您想等待他们自行退出,您可以使用p.WaitForExit() - 但是,如果您一次只等待一个进程,这将仅适用。如果你想同时等待它们,它会变得棘手。

通常,你会使用WaitHandle.WaitAll这一点,但因为没有办法让一个WaitHandle对象了System.Diagnostics.Process的,你不能做到这一点(严重的是,跆拳道是微软的想法?)。

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

您必须使用p/invoke来访问本机win32 WaitForMultipleObjects函数。

下面是一个示例(我已经测试和实际工作)

[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 

} 

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方法是关闭的过程是,将如何走。另外,我想你会想为每个命令启动一个线程,而不是等待可执行文件完成后才能继续执行下一个命令。

参考: 的使用关键字为IDisposable的对象:

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好一点,因为编译器会阻止您重新分配使用块内的作家的参考。

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

考虑提供方法关闭(),除了Dispose()中,如果关闭是在该地区的标准术语。


在您的代码示例中,外部的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(),因为代码开始变得更丑陋。阅读msdn文档以获取更多信息。