C#中的线程问题来自外部进程

5

我有这段简单的代码:

public void Run()
{
   var invokerThread = new Thread(new ThreadStart(RunOnBackground));
   invokerThread.Start();
}

private void RunOnBackground()
{
   Trace.WriteLine("hi");
   ...
}

很不幸,当运行这段代码(来自第三方进程)时,线程并没有真正运行。

在进程资源管理器和VS调试器中,我看到线程被创建并且状态为“Running”。

主线程的公寓是STA,我已经尝试了内部线程的STA和MTA两种方式。

当我在Run()方法的末尾添加invokerThread.Join();时,线程确实运行。但是这并没有真正帮助。

我错过了什么?

编辑:以下是有关代码托管的更多信息 -

Run()方法通过COM互操作从进程中调用,该进程也是托管可执行程序集(使用COM互操作的原因是系统中的所有其他组件都是本地的)。

RunOnBackground()方法在跟踪之后包含一些更多的代码,通常其执行时间为10-20秒,包括启动另一个进程并等待其终止。此外,在代码中我还有一些其他区域,其中我写一些调试信息到Trace中。在调试代码时,Run()像平常一样运行,在invokerThread.Start();之后,invokerThread的状态为“Running”(尽管RunOnBackground()方法内的断点不会停止)。

当我在Run()方法的末尾添加invokerThread.Join()时,调试器在Join()之后进入RunOnBackground()


不应在工作线程中显示MessageBox。改用Trace。 - Harvey Kwok
更新了代码,但还是不起作用。请看我写给马丁的响应。 - Aaron
你说:“不幸的是,当运行这段代码(来自第三方进程)时,线程并没有真正运行。” 你是如何“运行这段代码”的? - Eric Dahlvang
我认为这与您托管代码的方式有关。您能否提供有关托管过程的更多信息?另外,您说线程实际上并未运行,但在进程资源管理器中,该线程的状态处于“运行”状态。您的意思是您看到该线程正在运行,但没有看到跟踪输出吗?您是否设置了断点以验证该线程确实正在运行? - Harvey Kwok
编辑了问题:添加了更多描述。 - Aaron
2个回答

3

关于RunOnBackground()函数的真正作用,有一些关键信息缺失。除此之外,当您在工作线程上使用单元线程COM对象时,这是一个很好的匹配。COM会自动将这种对象上的任何方法调用从工作线程调度到创建它的STA线程。

只有当STA线程遵守STA线程要求时,才能很好地工作。它必须泵送消息循环并且不能阻塞。违反这些规则会导致死锁的可能性非常大,工作线程调用无法完成,直到STA线程分派已调度的调用。看到Thread.Join()解决问题是这样做的确定迹象。当在STA线程上调用时,CLR内部会自动泵送消息循环。

要诊断此问题,您需要使用Debug + Windows + Threads查看工作线程正在阻塞的内容。如果我的猜测是正确的,那么它将深深地埋在COM管道代码中,等待调度已调度的调用完成。只有启用非托管代码调试并设置Microsoft符号服务器,才能看到这一点,从而获得管道代码的调试符号和可靠的堆栈跟踪。

修复这个问题将会很困难。当代码明确声明不支持多线程时,你不能通过神奇地翻转开关来让代码在一个线程上运行。必须在调用其方法的同一线程上创建 COM 对象的实例,并且该线程必须是 STA 线程。查看此 示例代码 了解方法。如果无法控制 COM 对象的创建,则无能为力。

Hans,感谢您的简短回答。您写道:“...看看那个工作线程阻塞在哪里。”实际上,在调试过程中,我看到在Start()之后,工作线程的状态已经改变为“运行”。RunOnBackground()方法调用其他方法,其中一个方法启动另一个进程并等待其退出,但我也尝试通过注释掉RunOnBackground()方法中的所有内容并编写一些简单的Trace.Write()和一些赋值(例如var x = 1;)来简化场景。 - Aaron

0

我可能会说些愚蠢的话,但这是我在MSDN Threads中看到的。

看看末尾的示例部分。

示例的输出很有趣,你可以看到只有当主线程执行Sleep(0)或Thread.Join()时,创建和启动的线程才开始执行。

这似乎正是你所遇到的问题,不是吗?

也许在你的主线程上使用Sleep(0)来真正启动你的工作线程。

另一个解决方法是使用BackGroundWorker

顾名思义,它在后台工作,非常容易使用。它可能对你非常有用。


尝试使用Thread.Sleep(),但没有帮助。BackgroundWorker也无法启动。 - Aaron

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