【问题标题】:How to check state of one thread from another in a thread safe way?如何以线程安全的方式检查一个线程的状态?
【发布时间】:2015-07-16 07:13:49
【问题描述】:

背景:

在调试应用程序时,我遇到了这个功能

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

我认为这是一个错误,因为在 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 语句上放置了断点,很快就看到了我的怀疑。当代码到达第一个断点时 Thread t 的 ThreadState 为 WaitSleepJoin 并且当我将代码单步执行到下一个 if 语句时 Thread t 已切换状态并具有 Running 的 ThreadState ,因此两个测试均失败并从该方法返回 false应该返回 true。

我的问题:

我怎样才能以线程安全的方式从另一个线程检查一个线程的状态?

我的想法和我的研究:

  1. 如果我可以使语句原子化,它会起作用。我在 Google 上搜索了一些内容,例如“如何在 c# 中使语句成为原子”,并得到了很多涉及锁的结果——这不是我想要的,因为据我所知,锁不会使代码原子化。

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

  3. 以某种方式更改线程 t 以实现同步。不是一个选择。该代码是可重用的代码,已被不同的客户端以不同的方式重用,线程 t 是客户端代码。不可能更改所有客户端。

我不是多线程专家,所以如果我的任何假设不正确或我对多线程的理解不正确,请随时指出。

【问题讨论】:

  • 如果线程在所有检查“goodness”的点上都是“good”,则当前函数返回 true。我不确定找到一个原子解决方案是否有意义,如果确实有的话,因为在函数返回 true 后线程可能立即变得不好。大概此代码适用于已知线程不会以任何频率从良好切换到不良的场景。
  • var state = t.ThreadState; return state == ThreadState.Running || state == ThreadState.WaitSleepJoin;
  • @Eric J 该方法用于从队列中移除未运行的线程。如果该方法在队列中留下一个未运行的线程,那没关系,因为下次运行该方法时它将被删除。但是尝试删除仍在运行的线程会导致问题。
  • 那么当前的代码应该没问题。它必须通过所有“好”检查才能仍然被认为是“好”。如果它没有通过任何一项检查,它就没有运行,并且不会自行返回到运行状态。
  • 旁注:实现合理工作的线程池很难。您可能需要考虑依赖框架提供的工具 - 即带有 async/await 的任务可能是合适的。

标签: c# multithreading


【解决方案1】:

我很确定状态为RunningWaitSleepJoin 的线程不可能是不活动的。所以你可以只获取状态(这是一个原子操作),然后检查这两个值:

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

请注意,the documentation 明确指出您应该只将线程状态用于调试目的,而不是用于同步:

线程状态只对调试场景感兴趣。您的代码不应该使用线程状态来同步线程的活动。

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

【讨论】:

  • 请注意,线程的真实状态在将其分配给局部变量state 后可能会立即发生变化。方法调用 IsThreadGood 不是原子的,在返回语句点指示线程的真实状态的意义上。但是,根据 OP 关于如何在问题的 cmets 中使用代码的描述,这应该无关紧要。
【解决方案2】:

这永远行不通。线程可能处于最后一条指令,而您的检查将错过接近且保证的退出。线程必须与您合作。您需要在更高级别进行同步。我不能建议如何,因为我不知道该应用程序的作用。提出一个包含详细信息和代码的新问题。

请注意,t.IsAlive && t.ThreadState == ThreadState.Running || t.ThreadState == ThreadState.WaitSleepJoin 缺少 || 运算符周围的大括号。这并不重要,因为必须丢弃此代码。

state == ThreadState.WaitSleepJoin 应该完成什么?这种状态可能是虚假的,因为许多图书馆可能会进入这种状态。这一切似乎都是假的。

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

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

【讨论】:

    【解决方案3】:

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

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

    出于显而易见的原因,它应该从另一个线程调用。

    【讨论】:

    • 这并不能解决问题。他已经可以检测到一个已经结束的线程。
    • 他已经尽可能可靠地检测到线程退出。问题是线程可以在检查后立即退出。仅当检测到退出时,检查才有意义。
    • @usr IsThreadGood() 当状态从 WaitSleepJoin 切换到 Running 时,OP 可能会错误地返回 false。
    • 我明白了。好的,让我们等待 OP 的回应他的问题到底是什么。也许这就是他要找的东西。那么答案很好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-30
    • 1970-01-01
    • 2013-08-29
    • 2020-06-05
    • 1970-01-01
    相关资源
    最近更新 更多