我正在使用的Windows服务应用程序遇到一些奇怪的行为。这是我第一次接触Tasks,所以我的学习曲线很陡,需要一些帮助,因为我知道我的问题很可能是由于我误解了。
我有以下设置:
public partial class MyService
{
protected override void OnStart(string[] args)
{
MasterTokenSource = new CancellationTokenSource();
MasterCancellationToken = MasterTokenSource.Token;
//Begin tasks.
StartAllTasks();
//This is the thread that is going to listen for updates in the database.
Task MasterService = Task.Factory.StartNew(() =>
{
while (!MasterCancellationToken.IsCancellationRequested)
{
//Sleep for the amount of time as determined in the DB
Thread.Sleep(ServiceInstance.PollInterval * 1000);
Console.WriteLine("Polled for changes");
//Check service modules for changes as per DB config
UpdateServiceModulePropertiesAndRunningTasks();
//MasterTokenSource.Cancel();
}
MasterCancellationToken.ThrowIfCancellationRequested();
}, MasterCancellationToken);
}
private void StartAllTasks()
{
//Index pages task
ServiceModule PageIndexersm = ServiceInstance.GetServiceModule("PageIndexer");
PageIndexer.StartNewInstance(PageIndexersm, ConfigInstance, MasterTokenSource);
//There are other calls to other methods to do different things here but they all follow the same logic
}
private void UpdateServiceModulePropertiesAndRunningTasks()
{
//Get a fresh copy of the service instance, and compare to current values
ServiceInstance compareServiceInstance = new ServiceInstance(ConfigInstance.OneConnectionString, ConfigInstance.TwoConnectionString, ConfigInstance.ServiceName);
foreach (ServiceModule NewServiceModuleItem in compareServiceInstance.AllServiceModules)
{
ServiceModule CurrentServiceModuleInstance = ServiceInstance.GetServiceModule(NewServiceModuleItem.ModuleName);
if (!NewServiceModuleItem.Equals(CurrentServiceModuleInstance))
{
//Trigger changed event and pass new instance
CurrentServiceModuleInstance.On_SomethingChanged(NewServiceModuleItem, MasterTokenSource);
}
}
}
}
public class PageIndexer
{
public ServiceConfig ServiceConfig { get; set; }
public ServiceModule ServiceModuleInstance { get; set; }
public Guid InstanceGUID { get; set; }
public CancellationTokenSource TokenSource { get; set; }
public CancellationToken Token { get; set; }
public PageIndexer(ServiceModule PageIndexerServiceModule, ServiceConfig _ServiceConfig)
{
ServiceModuleInstance = PageIndexerServiceModule;
ServiceModuleInstance.SomethingChanged += ServiceModuleInstance_SomethingChanged;
ServiceConfig = _ServiceConfig;
InstanceGUID = Guid.NewGuid();
}
//This is the method called within the PageIndexer instance
private void ServiceModuleInstance_SomethingChanged(ServiceModule sm, CancellationTokenSource MasterCancelToken)
{
Console.WriteLine(InstanceGUID + ": Something changed");
TokenSource.Cancel();
//Start new indexer instance
PageIndexer.StartNewInstance(sm, ServiceConfig, MasterCancelToken);
}
public void RunTask()
{
Console.WriteLine("Starting Page Indexing");
Task.Factory.StartNew(() =>
{
while (true)
{
if (TokenSource.Token.IsCancellationRequested)
{
Console.WriteLine(InstanceGUID + ": Page index CANCEL requested: " + TokenSource.IsCancellationRequested);
TokenSource.Token.ThrowIfCancellationRequested();
}
if (ServiceModuleInstance.ShouldTaskBeRun())
{
Console.WriteLine(InstanceGUID + ": RUNNING full index, Cancellation requested: " + TokenSource.IsCancellationRequested);
RunFullIndex();
}
else
{
Console.WriteLine(InstanceGUID + ": SLEEPING, module off, Cancellation requested: " + TokenSource.IsCancellationRequested);
//If the task should not be run then sleep for a bit to save resources
Thread.Sleep(5000);
}
}
}, TokenSource.Token);
}
public static void StartNewInstance(ServiceModule serviceModule, ServiceConfig eServiceConfig, CancellationTokenSource MasterCancellationToken)
{
PageIndexer pageIndexerInstance = new PageIndexer(serviceModule, eServiceConfig);
CancellationTokenSource NewInstanceCancellationTokenSource = new CancellationTokenSource();
NewInstanceCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(MasterCancellationToken.Token);
pageIndexerInstance.TokenSource = NewInstanceCancellationTokenSource;
pageIndexerInstance.Token = pageIndexerInstance.TokenSource.Token;
pageIndexerInstance.RunTask();
}
}
我看到的是,对于检测到的第一个更改,取消和开始对我来说工作正常,但是在其他更改不起作用之后发出的后续取消不起作用。我可以看到对事件方法的调用正在发生,但是,它似乎正在对页面索引器的原始实例进行调用。
我确定我已经走了很长时间,已经彻底搞砸了,但是我很感谢任何人都可以提供的指导,让我回到正确的轨道上
谢谢。
致谢
答案 0 :(得分:0)
CancellationTokenSource
和CancellationToken
只能发信号一次。他们永远被取消。如果您需要多个线程/任务的多个取消信号,则每个此类操作都需要一个令牌。
通常,将它们分组在一个类中是一个很好的模式:
class MyOperation {
Task task; //use this for waiting
CancellationTokenSource cts; //use this for cancelling
}
这样,任务和令牌将自动建立1:1关联。您可以通过这种方式取消特定任务。