如何创建具有连续循环的线程/任务

本文关键字:循环 连续 线程 任务 何创建 创建 | 更新日期: 2023-09-27 18:08:56

我正在寻找在Thread/Task中创建循环的正确方法/结构…

这样做的原因是,我需要每15秒检查一次数据库的报告请求。

这是我尝试到目前为止,但我得到OutOfMemoryException:

    private void ViewBase_Loaded(object sender, RoutedEventArgs e)
{
    //On my main view loaded start thread to check report requests.
    Task.Factory.StartNew(() => CreateAndStartReportRequestTask());
}
private void CreateAndStartReportRequestTask()
{
    bool noRequest = false;
    do
    {
         //Starting thread to Check Report Requests And Generate Reports
         //Also need the ability to Wait/Sleep when there are noRequest.
         reportRequestTask = Task.Factory.StartNew(() => noRequest = CheckReportRequestsAndGenerateReports());
         if (noRequest)
         {
             //Sleep 15sec
             reportRequestTask.Wait(15000);
             reportRequestTask = null;
         }
         else
         {
             if (reportRequestTask.IsCompleted)
             {
                 reportRequestTask = null;
             }
             else
             {
                 //Don't want the loop to continue until the first request is done
                 //Reason for this is, losts of new threads being create in CheckReportRequestsAndGenerateReports()
                 //Looping until first request is done.
                 do
                 {
                 } while (!reportRequestTask.IsCompleted);
                 reportRequestTask = null;
             }
         }
    } while (true);
}
private bool CheckReportRequestsAndGenerateReports()
{
    var possibleReportRequest = //Some linq query to check for new requests
    if (possibleReportRequest != null)
    {
        //Processing report here - lots of new threads/task in here as well
        return false;
    }
    else
    {
        return true;
    }
}

我做错了什么?

这是正确的方法还是我完全错了?

编辑:

最重要的是,我的UI必须仍然是响应!

如何创建具有连续循环的线程/任务

可以这样做:

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

Repeat类是这样的:

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);
    }
}

听起来你想要这样的东西。如果我误解了你的意思,请纠正我。

首先,在启动中,设置为长时间运行的任务,这样它就不会从线程池中消耗线程,而是创建一个新线程…

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();
}

然后,在您的报告监视线程中,循环直到IsCancelRequested已设置。如果没有工作,只需等待取消令牌15秒(这样如果取消将更快唤醒)。

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);
        }
    }
}

我也会小心让你的任务引发更多的任务。我有一种感觉,你正在旋转太多,你消耗了太多的资源。我认为你的程序失败的主要原因是:

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

这将立即返回,而不是等待15秒,因为此时线程已经完成。将其切换为取消令牌(或Thread.Sleep(),但这样您就不能轻易地中止它)将为您提供所需的处理等待时间。

希望这对你有帮助,如果我的假设不对,请告诉我。

我从@Roger的回答开始做了一个变通。(我的一个朋友也给出了很好的建议)…我把它复制到这里,我想它可能有用:

/// <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);
    }
}

想冒险吗?

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);
    }
}