Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/292.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在Windows服务中使用线程无限循环_C#_Multithreading_Windows Services - Fatal编程技术网

C# 在Windows服务中使用线程无限循环

C# 在Windows服务中使用线程无限循环,c#,multithreading,windows-services,C#,Multithreading,Windows Services,我在网上看到过几篇与我的问题相似的帖子,但都没有解决我的问题。我正在创建一个windows服务,它将每隔几秒钟左右轮询一个Redis数据库,并根据结果执行一个操作。我想创建一个“线程池”,这样,如果在处理另一个命令(在另一个线程上)时从Redis获得结果,我就可以同时运行多个操作 我的一个主要问题是,当我停止我的Windows服务时,进程仍然保持活动状态约30秒,而不是关闭。以下是相关的代码片段: Thread Worker; IDatabase db = ...; AutoResetEvent

我在网上看到过几篇与我的问题相似的帖子,但都没有解决我的问题。我正在创建一个windows服务,它将每隔几秒钟左右轮询一个Redis数据库,并根据结果执行一个操作。我想创建一个“线程池”,这样,如果在处理另一个命令(在另一个线程上)时从Redis获得结果,我就可以同时运行多个操作

我的一个主要问题是,当我停止我的Windows服务时,进程仍然保持活动状态约30秒,而不是关闭。以下是相关的代码片段:

Thread Worker;
IDatabase db = ...;
AutoResetEvent StopRequest = new AutoResetEvent(false);

protected override void OnStart(string[] args) {
    var poller = new Poller();
    Worker = new Thread(() => poller.Poll(StopRequest));
    Worker.Start();
}
protected override void OnStop() {
    // Signal worker to stop and wait until it does
    StopRequest.Set();
    Worker.Join();
}
下面是
Poller
classes
Poll
方法的一个示例

public async void Poll(AutoResetEvent finished)
{
    var res = string.Empty;

    while (!finished.WaitOne(1000))
    {
        res = db.StringGet($"task");
        if (!String.IsNullOrEmpty(res))
        {
            ParseAction(res);
        }

        db.KeyDelete($"task");
    }
}
因此,这段代码(经过大量删减)保持在后台正确运行,并且似乎可以很好地处理来自Redis的传入查询,但正如我前面提到的,我遇到了进程无法正确关闭的问题。我也不确定这是否是应对这种情况的最佳方法。我想要一些更好或更“惯用”的方法来处理这个线程问题


谢谢

您是否考虑过使用布尔/二进制标志来确定服务是否正在运行?或者在循环开始时执行调用以进行检查?为了全面理解手头的整个任务,我对C#还不够熟悉,但我知道,当涉及多线程时,二进制/布尔标志通常相当稳定

例如,我玩了一个测试版(太空工程师)的Steam游戏,它使用C#,每次执行后似乎总是出现多线程错误和清除父数据的问题,但是Steam Workshop上的Mod作者倾向于使用布尔和二进制标志,以确保他们的任务不会被卡住或崩溃,因为重新启动游戏的加载时间非常可怕,因此他们尝试尽可能避免崩溃

这可能不是什么幻想,但只要您确保它不会造成失控的内存泄漏,您就可以了。我建议,如果为每个任务的唯一标识符使用增量变量,则在某处显式设置上限,当达到该上限时,它将调用该任务并将增量变量重置为零(具有大量缓冲空间以防止意外数据丢失)

如果任务正在运行,它将执行调用、设置布尔值并执行,在尝试写入目标之前,可能需要另一个调用来验证任务是否仍在运行,因为我假设没有任务,信息不会起任何作用,如果任务没有运行,它将深入研究
If!=正在运行
,并被发送到正确的目标以终止线程


我希望这些信息对您有所帮助,正如我前面提到的,我只是C#的初学者,所以我对高级命令不象这里的其他一些用户那样熟悉。

您是否考虑过使用布尔/二进制标志来确定服务是否真的在运行?或者在循环开始时执行调用以进行检查?为了全面理解手头的整个任务,我对C#还不够熟悉,但我知道,当涉及多线程时,二进制/布尔标志通常相当稳定

例如,我玩了一个测试版(太空工程师)的Steam游戏,它使用C#,每次执行后似乎总是出现多线程错误和清除父数据的问题,但是Steam Workshop上的Mod作者倾向于使用布尔和二进制标志,以确保他们的任务不会被卡住或崩溃,因为重新启动游戏的加载时间非常可怕,因此他们尝试尽可能避免崩溃

这可能不是什么幻想,但只要您确保它不会造成失控的内存泄漏,您就可以了。我建议,如果为每个任务的唯一标识符使用增量变量,则在某处显式设置上限,当达到该上限时,它将调用该任务并将增量变量重置为零(具有大量缓冲空间以防止意外数据丢失)

如果任务正在运行,它将执行调用、设置布尔值并执行,在尝试写入目标之前,可能需要另一个调用来验证任务是否仍在运行,因为我假设没有任务,信息不会起任何作用,如果任务没有运行,它将深入研究
If!=正在运行
,并被发送到正确的目标以终止线程


我希望这些信息对您有所帮助,正如我前面提到的,我只是C#的初学者,因此我对高级命令的熟悉程度不如这里的其他一些用户。

处理Windows服务的更好方法是将整个处理过程移到后台任务中。这将允许您更优雅地处理启动和关闭

如果使用Task模拟轮询,则可以使用CancellationToken将关闭事件传播到其他处理层。在这里,您可以找到如何使用任务模拟计时器。请阅读

下面是windows服务OnStart和OnStop处理程序的代码示例,这些处理程序具有快速启动和关闭的后台任务。此代码基于.NET 4.6.1

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.ServiceProcess;

namespace TEST.MY.SERVICE
{
    partial class MyService : ServiceBase
    {
      private Task _initializationTask;
      private CancellationTokenSource _initializationCancelTokenSource;
      private CancellationToken _intitializationCancellationToken;

      public MyService()
      {
          InitializeComponent();
      }

      protected override void OnStart(string[] args)
      {
        _initializationCancelTokenSource = new CancellationTokenSource();
        _intitializationCancellationToken = _initializationCancelTokenSource.Token;
        _initializationTask = Task.Run(() =>
        {
          //Kick off polling from here that also uses _intitializationCancellationToken, so that when _initializationCancelTokenSource.Cancel() is invoked from OnStop it will start cancellation chain reaction to stop all running activity. You can pass it even into your methods and check _intitializationCancellationToken.IsCancellationRequested and take appropriate actions.

                //using the Task timer from the other stack overflow post, You could do something like
                Task perdiodicTask = PeriodicTaskFactory.Start(() =>
                {
                    Console.WriteLine(DateTime.Now);
                    //execute your logic here that has to run periodically
                }, intervalInMilliseconds: 5000, // fire every 5 seconds...
                   cancelToken: _intitializationCancellationToken); // Using same cancellation token to manage timer cancellation

                perdiodicTask.ContinueWith(_ =>
                {
                    Console.WriteLine("Finished!");
                }).Wait();

        }, _intitializationCancellationToken)
        .ContinueWith(t =>
        {
          //deal with any task related errors
        },TaskContinuationOptions.OnlyOnFaulted);
      }

      protected override void OnStop()
      {
        try
         {
           _initializationCancelTokenSource?.Cancel();
           _initializationCancelTokenSource?.Dispose();
           _initializationTask?.Dispose();
          }
          catch (Exception stopException)
          {
                    //log any errors
          }
      }
  }
}
在这里,您可以找到有关如何取消等待的任务的更多详细信息


这将使您对如何设计windows服务有一个很好的了解。为你的需要做必要的装饰。熟悉c#任务库。

处理Windows服务的更好方法是将整个处理过程移到后台任务中。这将允许您更优雅地处理启动和关闭

如果使用Task模拟轮询,则可以使用CancellationToken将关闭事件传播到其他处理层。在这里,您可以找到如何使用任务模拟计时器。请阅读

这里