2013-04-24 44 views
4

我整天都在排除故障。在做了一些research以及很多試驗和錯誤之後,似乎我已經能夠縮小問題的範圍,因爲我的電話process.Start()在定時器線程上不起作用。下面的代碼在主線程上運行時工作。在定時器回調中放入完全相同的代碼,並掛起。爲什麼?我如何使它與計時器一起工作?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()掛起,但只要我點擊任何鍵結束,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(); 
} 
+0

工作正常,如預期的那樣,應該如此。你需要嘗試這是另一臺機器或沒有加載任何crudware的虛擬機。 – 2013-04-25 02:13:08

+0

crudware以何種方式造成這種情況?任何想法如何識別罪魁禍首? – 2013-04-25 02:24:57

+0

@HansPassant我在另一臺筆記本上重複了這個問題。我已經發布了可以作爲測試運行的完整代碼。這會讓我相信這個問題不是貪污。 (除非我在兩個系統上都有相同的軟件) – 2013-04-25 11:33:58

回答

8

有很多關於這個問題的重複問題,沒有一個完全符合你的情況。您可以使用調試器的「調試+ Windows +線程」窗口來查看問題。找到計時器線程並雙擊它。看看Call Stack窗口看到:

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以獲取它。

+0

不適用於所有應用程序,但如果您只是在主線程上等待'Console.ReadKey()'退出,則可以將其交換爲'Console.In.Peek()'。 「在我的機器上工作」 – Jonno 2013-05-21 13:16:13