Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Process.Start()在后台线程上运行时挂起_C#_Multithreading_Timer - Fatal编程技术网

C# Process.Start()在后台线程上运行时挂起

C# Process.Start()在后台线程上运行时挂起,c#,multithreading,timer,C#,Multithreading,Timer,我一整天都在排除故障。在做了一些和大量的尝试和错误之后,我似乎能够将问题缩小到对process.Start()的调用在计时器线程上不起作用这一事实。下面的代码在主线程上运行时起作用。将完全相同的代码放入计时器回调,它将挂起。为什么?如何让它与计时器一起工作 private static void RunProcess() { var process = new Process(); process.StartInfo.FileName = "cmd"; process.

我一整天都在排除故障。在做了一些和大量的尝试和错误之后,我似乎能够将问题缩小到对
process.Start()
的调用在计时器线程上不起作用这一事实。下面的代码在主线程上运行时起作用。将完全相同的代码放入计时器回调,它将挂起。为什么?如何让它与计时器一起工作

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();
}
编辑

作为测试,我在另一台笔记本电脑上使用了完全相同的代码,我遇到了同样的问题。这是可以粘贴到控制台应用程序中的完整代码
process.Start()

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();
}

关于这个问题有很多重复的问题,没有一个完全符合你的情况。您可以使用调试器的“调试+窗口+线程”窗口查看问题。找到计时器线程并双击它。查看调用堆栈窗口以查看:

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...
该线程在Console.InputEncoding属性getter上处于死锁状态。Process类使用它来确定需要使用什么编码来将流程的重定向输出转换为字符串

这是针对.NET 4.5的,它还将影响安装了4.5的计算机上以4.0为目标的应用程序,因为它不是.NET的并行版本。死锁是由主线程中的Console.ReadKey()方法调用引起的。它现在获得一个锁,防止其他线程干扰控制台。这在微软软件中是一个相当全球性的变化,VS2012创建的C/C++应用中使用的CRT也添加了这个锁。确切的原因我还不太清楚,但肯定是因为当程序请求输入时,控制台输出不会与控制台输入混淆。InputEncoding属性也需要使用该锁的确切原因有点难以解释,但符合序列化控制台输入访问的模式。当然,这对许多程序员来说是一个巨大的惊喜,尤其是那些像你一样编写测试线程代码的小测试应用程序的程序员。TDD有点倒退

解决方法有点令人不快,就TDD而言,您必须停止使用Console.ReadKey()以避免死锁。真正的程序将使用AutoResetEvent的WaitOne()方法来知道工作线程已完成执行。或者CountDownEvent.Wait(),更符合多次尝试代码的要求。等等



更新:此死锁场景已在.NET 4.5的服务更新中解决。在您的计算机上启用Windows Update以获取它。

工作正常,正如预期的和应该的那样。你需要试试这是另一台机器或没有任何crudware加载的VM。crudware会以什么方式导致这种情况?知道如何识别罪犯吗?@HansPassant我在另一台笔记本电脑上复制了这个问题。我已经发布了可以作为测试运行的完整代码。这会让我相信这个问题并不严重。(除非我在两个系统上都有相同的crudware。)您的问题变得有点臭名昭著,它是针对.NET4.5的。通过修改控制台类,ReadKey()方法获得一个锁,以防止其他线程干扰控制台。您可以在工作线程的调用堆栈中很容易看到这一点,它在Console.inpunecoding属性getter上死锁,该属性也尝试获取相同的锁。关于这个僵局已经有很多问题了,我会尽力找出一个。解决方法是不调用ReadKey()。其他阻止线程的方法,比如WaitOne()调用,该调用表示worker已完成。这可以解释为什么在作为服务运行时,它会工作,但在我的控制台应用程序中则不会。一个缺点是:我的console应用程序的目标是.NET 4.0。它不适用于所有应用程序,但如果您只需在主线程上等待
console.ReadKey()
退出,就可以将其替换为
console.In.Peek()
。“在我的机器上工作”