0

我们有一项服务可以执行以下基本工作流程:

1) 启动、读取配置设置并在一个大循环中执行一些计算。

2)循环的每次迭代,它都需要能够检查服务是否被告知停止。它执行数据库提取、计算然后存储结果。我对代码在 SQL 事务中的执行情况没有信心,所以在这个阶段,很高兴假设我们只在每次迭代开始时检查服务停止。

3) 执行完所有迭代后,服务“休眠”一段时间。可能是5分钟。可能是12小时。它需要能够在这个睡眠期间“停止”!

目前,这是通过以下方式执行的:

private int threadSleepMinutes = 60;

private readonly Mutex mutTerminateService = new Mutex(false);

private Thread serviceThread;

private Thread serviceStopThread;

// Use this flag to allow the Start op to wait for serviceStopThread
// to get going before continuing to create the main loop thread
private volatile bool stopService = true;

public void Start()
{
    this.serviceStopThread = new Thread(this.RunServiceStopThread);
    this.serviceStopThread.IsBackground = true;
    this.serviceStopThread.Start();

    while (stopService)
    {
        Thread.Sleep(100);
    }

    // Some things renamed to anonymise... you get the idea!
    this.serviceThread = new Thread(this.BigLoopMethod);
    this.serviceThread.IsBackground = true;
    this.serviceThread.Start();
}

public void Stop()
{
    // Release the mutex to terminate the service
    serviceStopThread.Resume();

    // Wait 5s max
    int timeout = 5000;
    while (this.serviceThread.IsAlive && timeout > 0)
    {
        Thread.Sleep(100);
        timeout -= 100;
    }
}

private void RunServiceStopThread()
{
    // To guarantee the same thread takes the mutex
    // and releases it in dot net 4, do both ops in this single thread!
    // Dot net 4 the Start() and Stop() are now usually on different threads.
    mutTerminateService.WaitOne();
    stopService = false;

    // Suspend ourself
    serviceStopThread.Suspend();

    // Release the mutex
    mutTerminateService.ReleaseMutex();
}

public void BigLoopMethod()
{
    try
    {
      do
      {
          bool moreOperationsToGo = true; // Just dummy flags and 'stuff' methods here
          while (moreOperationsToGo && !mutTerminateService.WaitOne(0))
          {
              DoStuff();
          }

          // Using this mutex here to sleep nicely - event driven.
          // Gracefully continues after timeout and gracefully exits if 
          // triggered by the mutex.
      }
      while (!mutTerminateService.WaitOne(this.threadSleepMinutes * 60000));
    }
    catch (Exception ex)
    {
        // Exception handling & logging here
    }
}

现在我收到消息说 Suspend 和 Resume 已弃用。在我的情况下,我确切地知道暂停是在什么代码上运行的,因为调用本身就是暂停它的原因!恢复,我确切地知道它会做什么。这样做的唯一原因是互斥锁在 dot net 3.5 中的 Start() 和 Stop() 中运行良好,但 dot net 4.0 发生了变化,因此 Start() 和 Stop() 位于不同的线程中并且它们标记了解决方法已过时!

有没有一种不错的、过时的方法来做到这一点?

谢谢

4

2 回答 2

2

除非您使用互斥锁进行进程间通信,即从另一个进程中取消您的工作线程 - 我相信有一种更简单的方法可以在 .net 4.0 中实现具有取消功能的工作线程。您可以使用取消令牌,并等待超时 - 如果令牌被取消,它将发出信号。下面的完整解决方案(部分使用您的代码):

using System;
using System.Threading;

class App
{
  static void Main()
  {
    var t = new Test();
    t.Start();
    Thread.Sleep(10000);
    Console.WriteLine("aborting");
    t.Stop();
  }
}

class Test
{
private int threadSleepMinutes = 60;

private Thread serviceThread;

private CancellationTokenSource tokenSource;

public void Start()
{
    // Some things renamed to anonymise... you get the idea!
    this.tokenSource = new CancellationTokenSource();
    this.serviceThread = new Thread(this.BigLoopMethod);
    this.serviceThread.IsBackground = true;
    this.serviceThread.Start();
}

public void Stop()
{
    tokenSource.Cancel();

    // Wait 5s max
    int timeout = 5000;
    if (!serviceThread.Join(timeout))
    {
      serviceThread.Abort();
    }
}

public void BigLoopMethod()
{
    try
    {
      var token = tokenSource.Token;
      do
      {
          int operationsToGo = 4; // Just dummy flags and 'stuff' methods here
          while (operationsToGo > 0 && !token.IsCancellationRequested)
          {
              Console.WriteLine("work");
              Thread.Sleep(1000);//DoStuff();
              operationsToGo--;
          }
          Console.WriteLine("no more work");
      }
      while (!token.WaitHandle.WaitOne(this.threadSleepMinutes * 60000));
    }
    catch (Exception ex)
    {
        // Exception handling & logging here
    }
}
}
于 2013-05-20T21:37:57.243 回答
0

您不需要“停止”线程。start 方法触发的事实就BigLoopMethod足够了。您在停止时需要做的就是向互斥体发出信号,然后以适当的超时时间加入线程(Thread.Join() 将等待线程停止)。如果您的线程没有在适当的时间内加入以强制终止服务,我会建议线程中止的稳健性。

所以在伪代码中:

void Start() 
{
    OpenMutex();
    TakeMutex();
    KickOffMyThread();
}

void Stop();
{
    SignalMutex();
    if (!MyThread.Join(Timeout))
    {
        MyThread.Abort();
        Environment.Exit(1); // Die as thread won't join
    }
}

void MyThread()
{
    while (!TakeMutex(sleeptime)
    {
        DoLongWork();
    }
    //Thread was signalled, exiting.
}
于 2013-05-20T02:23:12.873 回答