如何以线程安全的方式从另一个线程检查一个线程的状态?

4

背景:

在调试一个应用程序时,我遇到了这个函数

boolean IsThreadGood(Thread t)
{
    return t.IsAlive && t.ThreadState == ThreadState.Running || t.ThreadState == ThreadState.WaitSleepJoin;
}

我认为这是一个bug,因为return语句中的逻辑比较不是原子操作,所以我认为线程t在表达式执行时可能会改变状态。为了检查这个想法,我将代码更改为以下代码,以便我可以设置断点:

bool IsThreadGood(Thread t)
{
    if (t.IsAlive)
    {
        if (t.ThreadState == ThreadState.Running)
        {
            return true;
        }
        if (t.ThreadState == ThreadState.WaitSleepJoin)
        {
           return true;
        }
    }
    return false;
}

我在内部if语句上放置了断点,很快就发现了我怀疑的问题。当代码到达第一个断点时,线程t的线程状态为WaitSleepJoin,当我跨越代码到下一个if语句时,线程t已经切换了状态,并且具有Running的线程状态,因此未通过两个测试并从方法返回false,而它应该返回true。

我的问题:

如何以线程安全的方式从另一个线程检查线程的状态?

我的想法和我所研究的内容:

  1. 如果我可以使语句原子化,那么它将起作用。我谷歌了一些关于“如何在c#中使语句原子化”的东西,得到了许多涉及锁定的结果 - 这不是我想要的,因为据我所知,锁定不能使代码原子化。

  2. 如果我可以从执行该方法的线程中暂停Thread t,那么我就可以安全地检查它的状态。我在这里找到的是已过时的函数Suspend和Resume。Microsoft强烈建议不使用这些函数。

  3. 以某种方式更改Thread t以实现同步。这不是一个选项。该代码是可重复使用的代码,已被不同的客户以不同的方式重用,而Thread t是客户端代码。更改所有客户端将是不可能的。

我不是多线程专家,因此如果我的假设不正确或我的多线程理解不正确,请随时指出。


3
如果在检查“好”的所有点上线程都是“好的”,则当前函数返回true。我不确定是否有意义找到原子解决方案,因为线程可能在函数返回true后立即变得不好。假定这段代码是为一个场景而设计的,在这个场景中,已知线程不会经常从好变成不好。 - Eric J.
var state = t.ThreadState; return state == ThreadState.Running || state == ThreadState.WaitSleepJoin; - Blorgbeard
@Eric J 这个方法用于从队列中移除未运行的线程。如果该方法在队列中留下了一个未运行的线程,那么这是可以接受的,因为下次运行该方法时它将被移除。但是,尝试移除仍在运行的线程会导致问题。 - astidham2003
那么当前的代码应该是没问题的。它必须通过所有的“好”的检查才能被认为是“好”的。如果它未能通过任何一个检查,它将无法运行并且不能自动返回到运行状态。 - Eric J.
1
附注:实现一个正常工作的线程池很难。您可能需要考虑依靠框架提供的工具 - 即使用async/await的任务可能更合适。 - Alexei Levenkov
3个回答

4

我非常确定一个状态为RunningWaitSleepJoin的线程不能是非活动的。因此,您只需获取线程状态(这是一个原子操作),然后检查其是否等于这两个值:

boolean IsThreadGood(Thread t)
{
    ThreadState state = t.ThreadState;
    return state == ThreadState.Running || state == ThreadState.WaitSleepJoin;
}

请注意,文档明确指出,您应仅在调试方案中使用线程状态,而不是用于同步:

线程状态仅在调试方案中才有意义。您的代码永远不应使用线程状态来同步线程的活动。

由于您对原子操作感兴趣,我怀疑您并非出于调试目的(否则您不需要关心它是否超级精确)。因此,您应该考虑以不同的方式解决实际问题。


1
请注意,将线程分配给您的本地变量“state”后,其真实状态可能会发生更改。 IsThreadGood方法调用不是原子的,无法在返回语句处指示线程的真实状态。但是,基于OP对问题评论中代码使用方式的描述,这应该并不重要。 - Eric J.

1

这永远不可能实现。线程可能处于最后一条指令,而您的检查会错过接近和保证的退出。线程必须与您合作。您需要在更高级别上进行同步。我无法建议如何操作,因为我不知道应用程序的工作方式。请提出一个带有详细信息和代码的新问题。

请注意,t.IsAlive && t.ThreadState == ThreadState.Running || t.ThreadState == ThreadState.WaitSleepJoin 错过了 || 操作符周围的括号。尽管这段代码必须被丢弃,但这并不重要。

state == ThreadState.WaitSleepJoin 的目的是什么?此状态可能会发生错误,因为许多库可能进入该状态。这一切似乎都是虚假的。

作为解决方法,您可以实现一个看门狗线程:

while (true)
 DetectExit();
 Sleep(100);

0

Thread.Join(int millisecondsTimeout)方法可用于安全地测试线程是否已完成。当线程不再活动时,它返回true。

 // 0 == don't wait.
 if (thread.Join(0))
 {
    // remove thread from the list
 }

出于明显的原因,它应该从另一个线程中调用。


这并没有解决问题。他已经能够检测到一个已经结束的线程了。 - usr
他已经可靠地检测到线程退出,尽可能可靠。问题在于线程可以在检查后立即退出。只有在检测到退出时,检查才有意义。 - usr
@usr的IsThreadGood()函数可能会在线程状态从WaitSleepJoin切换到Running时错误地返回false。 - alexm
我明白了。好的,让我们等待OP的回复,看看他实际上遇到了什么问题。也许这就是他正在寻找的答案。如果是这样,那么这是一个很好的答案。 - usr

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