在Timer回调方法中使用CancellationTokens

问题描述 投票:1回答:1

我正在实施一个Windows Service,它将执行一些昂贵的Database operations,例如从一些表中检索和存储数据到其他表,目的是创建摘要,处理报告的数据等。

我正在使用Timer object控制我希望操作发生的时间,这一切都很好,我的主要问题是如何去取消,当有人决定停止service,或Windows重新启动,任何调用OnStop()方法。

我的代码看起来像这样:

码:

tmr = new Timer(new TimerCallback(tmr_Tick), cTs.Token, 0, 1000);

我有一个CancellationTokenSource作为服务中名为cTs的全局变量。

当调用OnStop方法时,我只设置cTs.Cancel()

我的Callback看起来像这样:

打回来:

    private void tmr_Tick(object sender)
    {
        if (((CancellationToken)sender).IsCancellationRequested)
        {
            tmr.Change(Timeout.Infinite, Timeout.Infinite);
            return;
        }

        if (DateTime.Now.TimeOfDay.Equals(intervalRepetition))
            Task.Run(async () => { await ProcessData((CancellationToken)sender); });
    }

我的问题是关于TimerCallback,因为我没有找到任何有助于处理令牌的东西,我将令牌作为State object传递,但我不确定这是否是最佳方式,所以我正在寻找咨询。

IsCancellationRequested被设置时我该怎么办?我应该实施更好的退出策略,也许会抛出取消例外吗?

令牌传播到ProcessData方法是否正确?如果我从它上面返回,ProcessData方法将回答令牌?我认为它会,但我不完全确定,因为它的异步和所有这一切。

c# timer windows-services cancellation-token
1个回答
1
投票

您不仅需要取消当前的活动工作,还要等待所有活动任务对该取消做出反应并完成。在您当前的方法中,您执行此操作:

private readonly CancellationTokenSource _cts = new CancellationTokenSource();
public override void OnStop() {
    _cts.Cancel();
}

这对任何事都没有帮助,因为一旦请求取消,您将从OnStop方法返回,并且所有后台线程(由于Task.Run而在其上执行您的工作)将被杀死而无需等待它们完成。

因此,在从OnStop返回之前,您需要某种方式等待它们完成。一种方法是使用Monitor.WaitMonitor.Pulse,如下所示:

private readonly object _activeTasksLock = new object();
private int _activeTasks;
private CancellationTokenSource _cts = new CancellationTokenSource();
private async Task ProcessData(CancellationToken ct) {
    lock (_activeTasksLock) {
        // increment number of active tasks
        _activeTasks++;
    }
    try {
        // do stuff
        await Task.Delay(1000, ct);
        // check cancellation token and exit if necessary
    }
    finally {
        lock (_activeTasksLock) {
            // decrement and notify
            _activeTasks--;
            Monitor.Pulse(_activeTasksLock);
        }
    }
}

public override void OnStop() {
    // dispose\stop your timer here
    // then cancel
    _cts.Cancel();
    // then wait until all is done
    lock (_activeTasksLock) {
        while (_activeTasks > 0)
            Monitor.Wait(_activeTasksLock);
    }
}
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.