如何使用连续循环创建线程/任务?

Wil*_*lem 31 .net c# multithreading thread-safety multitasking

我正在寻找正确的方法/结构来创建一个循环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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

这是正确的方式还是完全关闭?

编辑:

最重要的是,我的用户界面仍然必须响应!

Rog*_*mbe 58

像这样的东西会起作用:

var cancellationTokenSource = new CancellationTokenSource();
var task = Repeat.Interval(
        TimeSpan.FromSeconds(15),
        () => CheckDatabaseForNewReports(), cancellationTokenSource.Token);
Run Code Online (Sandbox Code Playgroud)

这个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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @RogerLipscombe 纯粹的好奇心:为什么 for(;;) 突然消失了(真的)? (2认同)

Jam*_*are 21

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

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

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();
}
Run Code Online (Sandbox Code Playgroud)

然后,在您的报告中观看线程,循环直到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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

     if (noRequest)
     {
         reportRequestTask.Wait(15000);
         reportRequestTask = null;
     }
Run Code Online (Sandbox Code Playgroud)

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

希望这会有所帮助,请告诉我,如果我对我的假设不以为然.


Jua*_*uan 7

我从@ 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);
    }
}
Run Code Online (Sandbox Code Playgroud)