Как создать поток / задачу с непрерывным циклом?
Я ищу правильный способ / структуру для создания цикла в Thread/Task
...
причина этого в том, что мне нужно проверять DB каждые 15sec для запросов отчетов.
Это то, что я пробовал до сих пор, но я получаю 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;
}
}
что я делаю не так?
это правильный путь или я итог?
EDIT:
самое главное, мой пользовательский интерфейс должен по-прежнему реагировать!
4 ответов
звучит, как вы хотите, что-то вроде этого. Пожалуйста, поправьте меня, если я неправильно понял ваши намерения...
во-первых, в вашем старте установите как длительную задачу, чтобы она не потребляла поток из пула потоков, но создавала новый...
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;
}
это вернется немедленно и не будет ждать 15s, потому что поток уже завершен в этот момент. Переключение его на токен отмены (или Thread.Sleep()
, но тогда вы не можете прервать его так же легко) даст вам ожидание обработки вам нужно.
надеюсь, что это помогает, дайте мне знать, если я ошибаюсь в своих предположениях.
что-то вроде этого будет работать:
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);
}
}
Я сделал обход, начиная с ответа @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);
}
}