为什么线程会进入 WaitSleepJoin 状态?

5
我正在编写一个运行后台线程的单例类。下面是如何启动和维护这个后台线程的代码:
 private void EnsureBackgroundThread()
        {
            try
            {
                if (this.RunnerThread == null)
                    this.RunnerThread = new Thread(this.BackgroundRunner) { IsBackground = true };

                if (this.RunnerThread.ThreadState != ThreadState.Running)
                {
                    Debug.WriteLine("----ApplePushNotificatorService.EnsureBackgroundThread ThreadState: " + this.RunnerThread.ThreadState);
                    this.RunnerThread.Start();
                }
            }
            catch (Exception ex)
            {
                this.LoggerService.Log(null, ex);
            }
        }

我在这个类中调用我的方法,像这样从TestClass中调用:
apns.Send("dev", devices, "Testing...", out badIds);

            // Wait 5 seconds to let it send stuff through.
            Thread.Sleep(5000);

            devices.Clear();
            devices.Add("some bad id");

            // Now let's call this again, but this time we should get back some bad Ids
            apns.Send("dev", devices, "Testing...", out badIds);

            // Wait 5 seconds to let it send stuff through.
            Thread.Sleep(5000);

            devices.Clear();
            devices.Add("9edc21d0d4e369f50040c5d2c94f2ea29c7d596090e4ddae253712cd406391df");
            apns.Send("dev", devices, "Test message for Andrew's phone", out badIds);

            // Wait 5 seconds to let it send stuff through.
            Thread.Sleep(5000);

我查看了错误日志并发现异常:
线程正在运行或已终止,无法重新启动。
在调试中,显示:
----ApplePushNotificatorService.EnsureBackgroundThread ThreadState: Background, WaitSleepJoin
为什么会进入“WaitSleepJoin”状态?是因为我在测试中使用了“Thread.Sleep”吗?
我的保持线程活动的代码是否正确?我该如何解决这个问题?我的想法是当单例上调用“Send”方法时,我们需要确保后台线程正在运行。
编辑:
以下是经过重新设计的代码,现在可以正常工作。
private void EnsureBackgroundThread()
        {
            try
            {
                if (this.RunnerThread != null && this.RunnerThread.IsAlive) return;
                this.RunnerThread = new Thread(this.BackgroundRunner) { IsBackground = true };
                this.RunnerThread.Start();
            }
            catch (Exception ex)
            {
                this.LoggerService.Log(null, ex);
            }
        }

2
使用IsAlive而不是比较ThreadState。有很多原因可能导致线程尚未完成工作时就不再运行(包括您的Thread.Sleep调用,但也包括执行lock或调用同步I/O时)。此外,多线程不应该基于试错的基础上进行。您应该了解发生了什么,否则您几乎肯定会得到意外的行为。 - Luaan
1
@Luaan 做那个,你最终仍会在线程终止后尝试启动该线程,这也是一个错误。 - Servy
@Servy 是的,你说得对。然而,潜在的问题是,OP不知道如何正确地处理异步(或并行)操作。 - Luaan
@katit 没有任何损失。保持线程活动的通常方法是在内部使用无限循环(根据您的用例,可以选择等待)。但是,在您的情况下,我想知道您是否真的需要后台任务。您不能使用 await / async 吗?这样做有什么原因吗? - Luaan
很遗憾,异步/等待不起作用。是的,我使用Sleep(100)进行无限循环。苹果规定连接到他们的推送服务器应该保持开放状态。因此,我必须在后台保持它旋转,并不断检查是否有新数据可用于发送。在这些公开属性周围使用锁定。 - katit
显示剩余2条评论
2个回答

5

该状态告诉我们线程目前正在睡眠,可能在您调用 Sleep 函数时。这意味着它仍在运行中。由于它仍在运行,您无法再次启动它。您正在尝试多次启动相同的线程。这是不允许的。您只能启动一次线程,然后它就开始了。试图第二次启动它,无论在它运行时还是完成后,都是不可能的。


2
简单来说,这个错误的意思是:你的线程已经被创建并正在运行或已终止,然后你又尝试启动它。
在你的 TestClass 中,可能有多次调用你的单例类(我猜测这可能是在 apns.Send 调用下面的某个地方)。第一次调用 EnsureBackgroundThread 时,将创建并启动一个线程。下一次调用 EnsureBackgroundThread 将在同一个线程上调用 Thread.Start,从而导致相同的错误。
这里可能需要注意的是,当一个线程完成时,引用它的变量不会设置为 null,但更可能的是你只是多次调用了 EnsureBackgroundThread 方法,你编写的代码不支持这样做。

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