【问题标题】:How to avoid deadlock when calling stop in a .net service from a thread?从线程调用.net服务中的停止时如何避免死锁?
【发布时间】:2018-03-13 10:45:51
【问题描述】:

我有一个用于从 TCP 套接字读取的线程;我从服务器收到一条消息以执行自动更新。所以,现在读取线程必须调用 servicebase Stop() 来触发 OnStop() 函数。然而,读取线程必须加入主线程才能正确终止服务。所以现在OnStop() 函数正在等待读取线程加入,但读取线程无法加入,因为它正在等待 Stop() 函数完成运行。

所以基本上它看起来像这样:

public void Start()
{
    OnStart(new string[0]);
}

protected override void OnStart(string[] args)
{
    stopEventRecv = new AutoResetEvent(false);
    RecvThread = new Thread(RecvLoop);
    RecvThread.Start();
}

protected override void OnStop()
{
    // Doesn't matter because we are about to deadlock
    stopEventRecv.Set();
    // Dead lock
    RecvThread.Join();
}

private void RecvLoop(object arg)
{
    while (true)
    {
        if (stopEventRecv.WaitOne(5000))
        {
            return;
        }

        if (!IsConnected())
            continue;

        // here we get message from server saying to Stop so
        // the message is processed and uses a callback where
        // ServiceBase.Stop() is called.
        // The reason for the callback isn't relevant to the
        // question I don't think but i'm mentioning it in case.
        // For the sake of the question I'll just call Stop()
        // here to demonstrate the problem.
        Stop();
    }
}

我该如何解决这个问题?退出服务前是否需要加入所有线程?在这里 Abort 可以代替加入吗?

【问题讨论】:

  • 我刚刚测试了 servicecontroller.stop,它似乎可以工作。
  • @Damien_The_Unbeliever 是的,你是对的,我错过了它确实只等待依赖服务。

标签: c# .net multithreading service


【解决方案1】:

服务控制管理器 (SCM) 负责向服务发送命令,告诉它们启动、停止、暂停,以及您选择定义的任何自定义命令。

您编写 OnXxx 方法来响应 SCM 发送给您的服务的命令。为了正确启动停止,您应该要求 SCM 停止您的服务,您可以使用 ServiceController 类的 Stop 方法来完成。这会启动 Stop 但不会等待它完成。


这可确保 SCM 知道您的服务正在故意停止并且没有因任何其他原因退出。这很重要,因为如果为您的服务配置了任何恢复操作,您不希望它们在这种情况下发生。

【讨论】:

    【解决方案2】:

    避免死锁的一个选择是不加入RecvThread,如果从它调用Stop。如果从该线程调用Stop - 你知道它已经完成了所有有用的工作并且即将退出,所以无论如何都不需要加入:

    if (RecvThread.ManagedThreadId != Thread.CurrentThread.ManagedThreadId)
        RecvThread.Join();
    

    另外,我个人会避免在没有超时的情况下执行Join。最好做Join(TimeSpan.FromSeconds(X)),如果给定线程在超时期间未能完成 - 做一些有意义的事情(抛出异常,中止该线程等等)。任何事情都比在不知不觉中陷入僵局要好。

    但是 Damian 在 cmets 中提到了更好的解决方案:使用 ServiceController.Stop 方法,该方法将停止所有依赖服务(如果有),然后将停止信号发送到您的服务并返回,而无需等待服务实际停止,因此也避免了死锁。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多