在后台线程中运行时的Process.Start()挂 [英] Process.Start() hangs when running on a background thread

查看:1593
本文介绍了在后台线程中运行时的Process.Start()挂的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经排除了一整天。做一些研究和大量的试验和错误之后,似乎我已经能够这个问题缩小的事实,我呼吁的Process.Start()不上一个计时器线程工作。主线程上运行时,下面的代码工作。把这些完全相同的代码在计时器回调,并且死了。为什么?我如何得到它具有定时工作?



 私有静态无效RunProcess()
{
VAR工艺=新工艺();

process.StartInfo.FileName =CMD;
process.StartInfo.Arguments =/ C出口;
process.StartInfo.UseShellExecute = FALSE;
process.StartInfo.RedirectStandardError = TRUE;
process.StartInfo.RedirectStandardInput = TRUE;
process.StartInfo.RedirectStandardOutput = TRUE;

的Process.Start(); //代码挂在这里,在后台线程

process.StandardOutput.ReadToEnd运行时,();

process.WaitForExit();
}

修改



作为测试,我用另一台笔记本电脑这种完全相同的代码,我经历了同样的问题。这是一个可以被粘贴到一个控制台应用程序完整代码。 的Process.Start()挂起,但只要我按任意键结束,的Process.Start()完成。节目结束之前

 私有静态System.Timers.Timer _timer; 
私人静态只读对象_locker =新的对象();

静态无效的主要(字串[] args)
{
ProcessTest();

Console.WriteLine(按任意键结束。);
Console.ReadKey();
}
私有静态无效ProcessTest()
{
初始化();
}
私有静态无效初始化()
{
INT timerInterval = 2000;
_timer =新System.Timers.Timer(timerInterval);
_timer.Elapsed + =新ElapsedEventHandler(OnTimerElapsed);
_timer.Start();
}
私有静态无效OnTimerElapsed(对象发件人,ElapsedEventArgs E)
{
如果{返回(Monitor.TryEnter(_locker)!); } //不要让这里的多个线程在同一时间。

{
RunProcess();
}
终于
{
Monitor.Exit(_locker);
}
}
私有静态无效RunProcess()
{
VAR过程=新工艺();
process.StartInfo.FileName =CMD;
process.StartInfo.Arguments =/ C出口;
process.StartInfo.UseShellExecute = FALSE;
process.StartInfo.RedirectStandardError = TRUE;
process.StartInfo.RedirectStandardInput = TRUE;
process.StartInfo.RedirectStandardOutput = TRUE;
的Process.Start(); // **挂这里**
process.StandardOutput.ReadToEnd();
process.WaitForExit();
}


解决方案

有许多重复的关于这个问题,没有说究竟适合你的情况的问题。您可以通过使用调试器的调试+的Windows +线程窗口中看到的问题。找到计时器线程,然后双击它。看看调用堆栈窗口看到:

  mscorlib.dll中System.Console.InputEncoding.get()+ 0x66字节
System.dll中!System.Diagnostics.Process.StartWithCreateProcess(System.Diagnostics.ProcessStartInfo的StartInfo)+ 0x7f5字节
System.dll中!System.Diagnostics.Process.Start()+均为0x88字节
ConsoleApplication70 .EXE!Program.RunProcess()线43 +字节是0xA C#
ConsoleApplication70.exe!Program.OnTimerElapsed(对象发件人,System.Timers.ElapsedEventArgs E)线28 + 0x5的字节C#
//等。 ..

线程被死锁在Console.InputEncoding属性的getter。其中所使用的工艺类弄清楚需要什么编码被用于该过程的输出重定向到字符串翻译。



这是具体到.NET 4.5,它也将影响已安装4.5一台机器上的目标4.0应用,因为它不是一个侧由端版本.NET的。死锁是通过在主线程Console.ReadKey()方法调用引起的。现在获得一个锁,防止其他线程与控制台搞乱。这一直是整个微软的软件相当全球变化,即在C / C中使用的CRT ++由VS2012创建的应用程序也增加了这个锁。确切的原因不是很清楚,我,但肯定有做与控制台输出没有得到与混合控制台输入,而你的程序要求输入一些东西。究竟为什么InputEncoding属性需要采取锁以及,没错,是有点难以解释,但适合顺序访问控制台输入的格局。当然,这是作为一个巨大的惊喜很多程序员,尤其是编写测试线程代码,像你这样的小测试应用程序的人。一个挫折TDD位。



解决方法是有点不愉快,TDD明智的,你必须停止使用Console.ReadKey(),以避免僵局。真正的方案将使用的AutoResetEvent的WaitOne的()方法来知道工作线程执行完毕。或CountDownEvent.Wait(),更符合尝试代码几次一致。 。诸如此类






更​​新:此死锁情况在.NET 4.5的服务更新得到解决。您的计算机上启用Windows更新来得到它。


I've been troubleshooting all day. After doing some research and a lot of trial and error, it seems I've been able to narrow down the issue to the fact that my call to process.Start() doesn't work on a timer thread. The code below works when running on the main thread. Put that exact same code in a timer callback, and it hangs. Why? How do I get it to work with a timer?

private static void RunProcess()
{
    var process = new Process();

    process.StartInfo.FileName = "cmd";
    process.StartInfo.Arguments = "/c exit";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.RedirectStandardInput = true;
    process.StartInfo.RedirectStandardOutput = true;

    process.Start();  // code hangs here, when running on background thread

    process.StandardOutput.ReadToEnd();

    process.WaitForExit();
}

EDIT

As a test, I used this exact same code on another laptop, and I experienced the same problem. This is complete code that can be pasted into a console app. process.Start() hangs, but as soon as I hit any key to end, process.Start() completes before the program ends.

private static System.Timers.Timer _timer;
private static readonly object _locker = new object();

static void Main(string[] args)
{
    ProcessTest();

    Console.WriteLine("Press any key to end.");
    Console.ReadKey();
}
private static void ProcessTest()
{
    Initialize();
}
private static void Initialize()
{
    int timerInterval = 2000;
    _timer = new System.Timers.Timer(timerInterval);
    _timer.Elapsed += new ElapsedEventHandler(OnTimerElapsed);
    _timer.Start();
}
private static void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
    if (!Monitor.TryEnter(_locker)) { return; }  // Don't let  multiple threads in here at the same time.
    try
    {
        RunProcess();
    }
    finally
    {
        Monitor.Exit(_locker);
    }
}
private static void RunProcess()
{
    var process = new Process();
    process.StartInfo.FileName = "cmd";
    process.StartInfo.Arguments = "/c exit";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.RedirectStandardInput = true;
    process.StartInfo.RedirectStandardOutput = true;
    process.Start();  // ** HANGS HERE **
    process.StandardOutput.ReadToEnd();
    process.WaitForExit();
}

解决方案

There are a lot of duplicate questions about this problem, none that exactly fits your case. You can see the problem by using the debugger's Debug + Windows + Threads window. Locate the timer thread and double-click it. Look at the Call Stack window to see:

mscorlib.dll!System.Console.InputEncoding.get() + 0x66 bytes    
System.dll!System.Diagnostics.Process.StartWithCreateProcess(System.Diagnostics.ProcessStartInfo startInfo) + 0x7f5 bytes   
System.dll!System.Diagnostics.Process.Start() + 0x88 bytes  
ConsoleApplication70.exe!Program.RunProcess() Line 43 + 0xa bytes   C#
ConsoleApplication70.exe!Program.OnTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) Line 28 + 0x5 bytes    C#
    // etc...

The thread is deadlocked on the Console.InputEncoding property getter. Which is used by the Process class to figure out what encoding needs to be used to translate the redirected output of the process into strings.

This is specific to .NET 4.5, it will also affect apps that target 4.0 on a machine that has 4.5 installed since it is not a side-by-side version of .NET. The deadlock is caused by the Console.ReadKey() method call in your main thread. Which now acquires a lock that prevents other threads from messing with the console. This has been a fairly global change across Microsoft software, the CRT that is used in C/C++ apps created by VS2012 also added this lock. The exact reason isn't that clear to me, but surely has to do something with console output not getting intermingled with console input while your program is asking for input. Exactly why the InputEncoding property needs to take that lock as well is, well, a bit hard to explain but fits the pattern of serializing access to console input. This of course comes as a big surprise to many programmers, especially the ones that write little test apps that test threaded code, like you did. Bit of a setback to TDD.

The workaround is a bit unpleasant, TDD wise, you do have to stop using Console.ReadKey() to avoid the deadlock. Real programs would use the WaitOne() method of an AutoResetEvent to know that the worker thread finished executing. Or CountDownEvent.Wait(), more in keeping with trying out code a couple of times. Etcetera.


UPDATE: this deadlock scenario was resolved in a service update for .NET 4.5. Enable Windows Update on your machine to get it.

这篇关于在后台线程中运行时的Process.Start()挂的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆