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

7

我有一些System.Diagnostics.Processes需要运行。我想自动调用它们的close方法。显然,“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
    }
}

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

3个回答

15
using(p = Process.Start(command))
这段代码可以编译通过,因为Process类实现了IDisposable接口,但是你实际上想要调用Close方法。
理论上说Dispose方法会自动调用Close方法,通过使用反射器查看CLR,我们可以看到它确实为我们执行了这个操作。目前为止还好。
再次使用反射器查看Close方法的功能 - 它释放底层的本地win32进程句柄并清除一些成员变量。这(释放外部资源)正是IDisposable模式应该做的事情。 但是我不确定这是否是你想在这里实现的。
释放底层句柄只是告诉Windows“我不再关心跟踪此其他进程”。它实际上没有导致其他进程退出,也没有导致您的进程等待。
如果您想要强制结束它们,您需要在进程上使用p.Kill()方法 - 但请注意,杀死进程从来都不是一个好主意,因为它们无法自我清理,可能会留下损坏的文件等等。
如果您想要等待它们自行退出,您可以使用p.WaitForExit() - 但是这只适用于一个进程一次等待。如果您想同时等待它们全部退出,这就有些棘手了。
通常,您会使用WaitHandle.WaitAll来实现此目的,但是由于无法从System.Diagnostics.Process中获取WaitHandle对象,因此无法完成此操作(说真的,微软在想什么?)。
您可以为每个进程启动一个线程,并在这些线程中调用`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

}

3

参考文献: IDisposable对象使用using关键字:

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

“is just compiler syntax.”的意思是这只是编译器语法。它编译成的内容是:

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

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

框架指南第9.3.1页256建议:

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


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

在这里使用可能不能实现您想要的效果,因为一旦p超出范围,就会调用Dispose()。这并没有关闭进程(已测试)。

进程是独立的,所以除非您调用p.WaitForExit(),否则它们会自行运行,与您的程序完全独立。

令人费解的是,对于进程,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 文档获取更多信息。


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方法关闭进程。此外,我认为你应该为每个命令启动一个线程,而不是等待可执行文件完成后再进行下一个。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接