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