Thread/Task
でループを作成する正しい方法/構造を探しています...
この理由は、レポートリクエストについて15秒ごとにDBをチェックする必要があるためです。
これは私がこれまで試したものですが、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がまだレスポンシブでなければならない!
このようなものが欲しいようです。あなたの意図を誤って解釈している場合は私を修正してください...
まず、キックオフで、長時間実行されるタスクとして設定します。これにより、スレッドプールからスレッドを消費せず、新しいスレッドを作成します...
_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()
ですが、その後簡単に中止できない)に切り替えると、必要な処理を待機できます。
これがお役に立てば幸いです、私の仮定に基づいてオフになっているかどうか教えてください。
このような何かが動作します:
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);
}
}