How to create a thread/Task with a continuous loop?

前端 未结 4 613
误落风尘
误落风尘 2020-12-22 19:25

I am looking for the correct way/structure to create a loop in a Thread/Task...

The reason for this is, i need to check the DB every 15sec for report re

相关标签:
4条回答
  • 2020-12-22 20:13

    Something like this would work:

    var cancellationTokenSource = new CancellationTokenSource();
    var task = Repeat.Interval(
            TimeSpan.FromSeconds(15),
            () => CheckDatabaseForNewReports(), cancellationTokenSource.Token);
    

    The Repeat class looks like this:

    internal static class Repeat
    {
        public static Task Interval(
            TimeSpan pollInterval,
            Action action,
            CancellationToken token)
        {
            // We don't use Observable.Interval:
            // If we block, the values start bunching up behind each other.
            return Task.Factory.StartNew(
                () =>
                {
                    for (;;)
                    {
                        if (token.WaitCancellationRequested(pollInterval))
                            break;
    
                        action();
                    }
                }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
        }
    }
    
    static class CancellationTokenExtensions
    {
        public static bool WaitCancellationRequested(
            this CancellationToken token,
            TimeSpan timeout)
        {
            return token.WaitHandle.WaitOne(timeout);
        }
    }
    
    0 讨论(0)
  • 2020-12-22 20:18

    feeling adventurous?

    internal class Program
    {
        private static void Main(string[] args)
        {
            var ct = new CancellationTokenSource();
    
            new Task(() => Console.WriteLine("Running...")).Repeat(ct.Token, TimeSpan.FromSeconds(1));
    
            Console.WriteLine("Starting. Hit Enter to Stop.. ");
            Console.ReadLine();
    
            ct.Cancel();
    
            Console.WriteLine("Stopped. Hit Enter to exit.. ");
            Console.ReadLine();
        }
    }
    
    
    public static class TaskExtensions
    {
        public static void Repeat(this Task taskToRepeat, CancellationToken cancellationToken, TimeSpan intervalTimeSpan)
        {
            var action = taskToRepeat
                .GetType()
                .GetField("m_action", BindingFlags.NonPublic | BindingFlags.Instance)
                .GetValue(taskToRepeat) as Action;
    
            Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    if (cancellationToken.WaitHandle.WaitOne(intervalTimeSpan))
                        break;
                    if (cancellationToken.IsCancellationRequested)
                        break;
                    Task.Factory.StartNew(action, cancellationToken);
                }
            }, cancellationToken);
        }
    }
    
    0 讨论(0)
  • 2020-12-22 20:24

    I've made a work-around starting from @Roger's answer. (A friend of mine has given good advices regarding this too)... I copy it here I guess it could be useful:

    /// <summary>
    /// Recurrent Cancellable Task
    /// </summary>
    public static class RecurrentCancellableTask
    {
        /// <summary>
        /// Starts a new task in a recurrent manner repeating it according to the polling interval.
        /// Whoever use this method should protect himself by surrounding critical code in the task 
        /// in a Try-Catch block.
        /// </summary>
        /// <param name="action">The action.</param>
        /// <param name="pollInterval">The poll interval.</param>
        /// <param name="token">The token.</param>
        /// <param name="taskCreationOptions">The task creation options</param>
        public static void StartNew(Action action, 
            TimeSpan pollInterval, 
            CancellationToken token, 
            TaskCreationOptions taskCreationOptions = TaskCreationOptions.None)
        {
            Task.Factory.StartNew(
                () =>
                {
                    do
                    {
                        try
                        {
                            action();
                            if (token.WaitHandle.WaitOne(pollInterval)) break;
                        }
                        catch
                        {
                            return;
                        }
                    }
                    while (true);
                },
                token,
                taskCreationOptions,
                TaskScheduler.Default);
        }
    }
    
    0 讨论(0)
  • 2020-12-22 20:24

    Sounds like you want something like this. Please correct me if I am misinterpretting your intentions...

    First, in your kick-off, set as a long running task so it doesn't consume a thread from the thread pool but creates a new one...

    private void ViewBase_Loaded(object sender, RoutedEventArgs e)
    {
        // store this references as a private member, call Cancel() on it if UI wants to stop
        _cancelationTokenSource = new CancellationTokenSource();
        new Task(() => CreateAndStartReportRequestTask(), _cancelationTokenSource.Token, TaskCreationOptions.LongRunning).Start();
    }
    

    Then, in your report watching thread, loop until IsCancelRequested has been set. If there is no work, just wait on the cancel token for 15 seconds (this way if cancelled will wake sooner).

    private bool CheckReportRequestsAndGenerateReports()
    {
        while (!_cancellationTokenSource.Token.IsCancelRequested) 
        {
            var possibleReportRequest = //Some linq query
            var reportRequestTask = Task.Factory.StartNew(() => noRequest = CheckReportRequestsAndGenerateReports(), _cancellationTokenSource.Token);
    
            if (noRequest)
            {
                // it looks like if no request, you want to sleep 15 seconds, right?
                // so we'll wait to see if cancelled in next 15 seconds.
                _cancellationTokenSource.Token.WaitHandle.WaitOne(15000);
    
            }
            else
            {
                // otherwise, you just want to wait till the task is completed, right?
                reportRequestTask.Wait(_cancellationTokenSource.Token);
            }
        }
    }
    

    I'd also be wary of having your task kick off more tasks. I have a feeling you are spinning up so many you're consuming too many resources. I think the main reason your program was failing was that you had:

         if (noRequest)
         {
             reportRequestTask.Wait(15000);
             reportRequestTask = null;
         }
    

    This will return immediately and not wait 15s, because the thread is already complete at this point. Switching it to the cancel token (or a Thread.Sleep(), but then you can't abort it as easily) will give you the processing wait you need.

    Hope this helps, let me know if i'm off on my assumptions.

    0 讨论(0)
提交回复
热议问题