简体   繁体   中英

Thread creation in method which is being called for several times using timer

I have a timer using which i am calling a method - {OnElapsedTime} And this should responsible for database access and updation for every interval of time.

protected override void OnStart(string[] args)
{
    try
    {
        ServiceLogFile("Service is started at " + DateTime.Now);
        timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
        timer.Interval = Int32.Parse(ConfigurationManager.AppSettings["tracktime"]); //number in miliseconds  
        timer.Enabled = true;
    }
    catch(Exception ex)
    {
        ServiceLogFile("Error in {OnStart} :" + ex.ToString());
    }

}
private void OnElapsedTime(object source, ElapsedEventArgs e)
{
    try
    {
        ServiceLogFile("Check for Database values - " + DateTime.Now);
        th = new Thread(new ThreadStart(Autocancellation));
        int ThreadID = Thread.CurrentThread.ManagedThreadId;
        ServiceLogFile("Thread ID = " + ThreadID);
        th.Start();
    }
    catch (Exception ex)
    {
        ServiceLogFile("Error in {OnElapsedTime} :" + ex.ToString());
    }
}

public void Autocancellation()
{
    try
    {
        lock (this)
        {
            //connection to database 
             //select Query and update 
        }
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message());
    }
}

If we can see the above code - I am creating a new thread for every OnElapsedTime call.

Please help me with creating a single thread outside of this method or anywhere and using the same instance inside {OnElapsedTime} whenever this method is being called (In my case - 5 seconds)

Assuming

  1. that we properly implement cooperative cancellation (otherwise there are no reliable ways to stop the currently running job)
  2. And we are not forced to use threads - https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/task-parallel-library-tpl
  3. And we are not required to run exactly once per 5 seconds (to accommodate the cooperative cancellation from bullet 1) - C# Timer elapsed one instance at a time

the simplest (or rather the simplest without any additional libraries ) way would be to use Task, TaskCancellationSource and a timer with AutoReset = false

public class Service
{
    private Task _job;
    private CancellationTokenSource _cancellationTokenSource;
    private readonly System.Timers.Timer _timer;

    public Service()
    {
        _timer = new System.Timers.Timer();
        {
            _timer.AutoReset = false;
            _timer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;    
            _timer.Elapsed += OnElapsedTime;
        }
    }

    public void Start()
    {
        Console.WriteLine("Starting service");
        _timer.Start();
    }

    private void OnElapsedTime(object source, ElapsedEventArgs e)
    {
        Console.WriteLine("OnElapsedTime");
        if (_job != null)
        {
            CancelAndWaitForPreviousJob();
        }
        Console.WriteLine("Starting new job");
        _cancellationTokenSource = new CancellationTokenSource();
        _job = Task.Run(
            () => ExecuteJob(_cancellationTokenSource.Token), 
            _cancellationTokenSource.Token);
        _timer.Start();
    }

    private void CancelAndWaitForPreviousJob()
    {               
         _cancellationTokenSource.Cancel();   
        try
        {
            Console.WriteLine("Waiting for job to complete");
            _job.Wait(
                millisecondsTimeout: 5000); // Add additional timeout handling?  
        }                
        catch (OperationCanceledException canceledExc)
        {
            Console.WriteLine($"Cancelled the execution: {canceledExc}");
        }     
        catch (Exception exc) 
        {                    
            Console.WriteLine($"Some unexpected exception occurred - ignoring: {exc}");
        }
    }

    private void ExecuteJob(CancellationToken cancellationToken)
    {
        Console.WriteLine("ExecuteJob start");
        try
        {
            for (var i = 0; i < 10; i++)
            {
                Console.WriteLine($"Job loop Iteration {i}");
                if (cancellationToken.IsCancellationRequested)
                {
                    Console.WriteLine("Cancellation requested - ending ExecuteJob");
                    return;
                }
                Thread.Sleep(1000);
            }
        }
        finally
        {
            Console.WriteLine("ExecuteJob end");
        }
    }
}

While it is a working solution you may be interested in Quartz.net - it has only a moderate learning curve and it is designed exactly for such scenarios.

PS: Also it seems that your application is a service based on https://docs.microsoft.com/en-Us/dotnet/api/system.serviceprocess.servicebase?view=netframework-4.8 . In such a case you may be interested in topshelf - it greatly simplifies a lot of things related to services.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM