【问题标题】:How to stop a Timer created in a .Net Core controller?如何停止在 .Net Core 控制器中创建的计时器?
【发布时间】:2020-08-18 12:32:29
【问题描述】:

我在 Api 控制器中声明了一个 System.Timers.Timer。

接下来有一个被 Javascript 客户端调用的 Action,它的任务是每秒向外部服务器发送一个 HTTP GET 请求,该请求发回一个 JSON。

然后 JSON 通过 WebSocket 发送到 Javascript 客户端。

我还创建了另一个在被调用时停止计时器的操作。

[Route("api")]
[ApiController]
public class PositionController : ControllerBase
{

    private System.Timers.Timer aTimer = new System.Timers.Timer();

    // ...

    // GET api/position/state
    [HttpGet("[controller]/[action]")]
    public async Task<string> StateAsync()
    {

        try
        {
            Console.WriteLine("In StateAsync (GET)");
            string json = "timer started";

            aTimer.Elapsed += new ElapsedEventHandler(async (sender, args) =>
            {

                json = await Networking.SendGetRequestAsync("www.example.com");

                Console.WriteLine($"Json in response:");
                Console.WriteLine(json);

                await _hubContext.Clients.All.SendAsync("ReceiveMessage", json);


            });
            aTimer.Interval = 1000;
            aTimer.Enabled = true;

            

            Console.WriteLine("----------------------------------");
            return json;

        }
        catch (HttpRequestException error) // Connection problems
        {
            // ...
        }
    }

    // GET api/position/stopstate
    [HttpGet("[controller]/[action]")]
    public async Task<string> StopStateAsync()
    {

        try
        {
            Console.WriteLine("In StopStateAsync (GET)");
            string json = "timer stopped";

            
            aTimer.Enabled = false;


            Console.WriteLine("----------------------------------");
            return json;

        }
        catch (HttpRequestException error) // Connection problems
        {
            // ...
        }
    }

     // ...

}

问题是,由于 ASP.NET 控制器(所以是 .Net Core 的?)gets instancieted for every new request,当我调用 Stop timer 方法时,计时器不会停止,因为它不是正确的 Timer 实例。于是系统继续发出HTTP请求和Websocket传输……

有没有办法保存和处理 Timer 实例,我需要从不同的 Controller 实例停止,或者我可以检索原始 Controller 实例吗?

提前谢谢大家:)

【问题讨论】:

  • 制作Timer属性static
  • 您可能最好有一个存储库(在内存中(单实例)或数据库中),您可以在其中存储这些与时间相关的项目。启动计时器时,它应该创建某种唯一标识符,该标识符由 StateAsync 操作返回,并在调用时传递给 StopStateAsync 操作。然后,您可以根据引用标识符查找正确的实例。另外,我宁愿不使用计时器,而只使用带有“开始”和“结束”时间戳的 POCO 对象。您没有取消订阅计时器事件,这不是一个好习惯。

标签: c# asp.net-core .net-core asp.net-core-webapi


【解决方案1】:

你真的应该让你的控制器做“控制器”的事情。在控制器中运行计时器会破坏控制器的模式。

你应该考虑实现一个IHostedService,当注入will maintain a timer时。

这是一个简单的例子:

TimerController.cs

[ApiController, Route("api/[controller]")]
public sealed class TimerController : ControllerBase
{
    private readonly ITimedHostedService _timedHostedService;

    public TimerController(ITimedHostedService timedHostedService)
    {
        _timedHostedService = timedHostedService;
    }

    // Just a tip: Use HttpPost. HttpGet should never change the
    // state of your application. You can accidentally hit a GET,
    // while POST takes a little more finesse to execute.
    [HttpPost, Route("startTimer/{milliseconds}")]
    public IActionResult StartTimer(int milliseconds)
    {
        _timedHostedService.StartTimer(milliseconds);
        return Ok();
    }

    [HttpPost, Route("stopTimer")]
    public IActionResult StopTimer()
    {
        _timedHostedService.StopTimer();
        return Ok();
    }

    [HttpGet, Route("isTimerRunning")]
    public IActionResult IsTimerRunning()
    {
        return Ok(new
        {
            result = _timedHostedService.IsTimerRunning()
        });
    }
}

TimedHostedService.cs

public interface ITimedHostedService
{
    void StartTimer(int milliseconds);
    void StopTimer();
    bool IsTimerRunning();
}

public sealed class TimedHostedService : IHostedService, ITimedHostedService
{
    private static Timer _timer;
    private static readonly object _timerLock = new object();

    public void StartTimer(int milliseconds)
    {
        lock(_timerLock)
        {
            _timer ??= new Timer(_ =>
            {
                // TODO: do your timed work here.
            }, null, 0, milliseconds);
        }
    }

    public bool IsTimerRunning()
    {
        lock(_timerLock)
        {
            return _timer != null;
        }
    }

    public void StopTimer()
    {
        lock(_timerLock)
        {
            _timer?.Change(Timeout.Infinite, Timeout.Infinite);
            _timer?.Dispose();
            _timer = null;
        }
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        StopTimer();
        return Task.CompletedTask;
    }
}

然后,像这样注入它:

services.AddHostedService<TimedHostedService>();
services.AddTransient<ITimedHostedService, TimedHostedService>();

我没有对此进行测试,但它应该可以正常工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-26
    • 2012-05-29
    • 1970-01-01
    相关资源
    最近更新 更多