I'm trying to save the number of times a trigger has fired into a database table but I can't find anything in the quartz-scheduler.net regarding this. Is there any method I can invoke to achieve this?
With simple trigger, there's a .GetTimesTriggered() method. Is there any equivalent for the cron triggers?
You can simply implement ISchedulerListener.
I have implemented it like following for some logging on trigger execution
public class QuartzSchedulerListener : ISchedulerListener//todo R & D to catch schdeuler error/invalid shutdown event and do possible implementation there
{
private static readonly log4net.ILog Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public Task JobScheduled(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobScheduled");
});
return t;
}
public Task JobUnscheduled(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobUnscheduled");
});
return t;
}
public Task TriggerFinalized(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler TriggerFinalized");
});
return t;
}
public Task TriggerPaused(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler TriggerPaused");
});
return t;
}
public Task TriggersPaused(string triggerGroup, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.DebugFormat("Quartz Scheduler TriggersPaused {0}", triggerGroup);
});
return t;
}
public Task TriggerResumed(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler TriggersPaused");
});
return t;
}
public Task TriggersResumed(string triggerGroup, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.DebugFormat("Quartz Scheduler TriggersResumed {0}", triggerGroup);
});
return t;
}
public Task JobAdded(IJobDetail jobDetail, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobAdded");
});
return t;
}
public Task JobDeleted(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobDeleted");
});
return t;
}
public Task JobPaused(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobPaused");
});
return t;
}
public Task JobInterrupted(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobInterrupted");
});
return t;
}
public Task JobsPaused(string jobGroup, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.DebugFormat("Quartz Scheduler JobsPaused {0}", jobGroup);
});
return t;
}
public Task JobResumed(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobResumed");
});
return t;
}
public Task JobsResumed(string jobGroup, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.DebugFormat("Quartz Scheduler JobsResumed {0}",jobGroup);
});
return t;
}
public Task SchedulerError(string msg, SchedulerException cause, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.DebugFormat("Quartz Scheduler is Error State message:{0}", msg);
if (cause != null)
{
Logger.Error(cause);
}
});
return t;
}
public Task SchedulerInStandbyMode(CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler is in Stand By Mode");
});
return t;
}
public Task SchedulerStarted(CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler is Started");
});
return t;
}
public Task SchedulerStarting(CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler is Starting");
});
return t;
}
public Task SchedulerShutdown(CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler is Shut Down");
});
return t;
}
public Task SchedulerShuttingdown(CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler is Shutting Down");
});
return t;
}
public Task SchedulingDataCleared(CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler SchedulingDataCleared");
});
return t;
}
}
StdSchedulerFactory factory = new StdSchedulerFactory();
var MyQuartzScheduler = await factory.GetScheduler();
QuartzSchedulerListener olistener = new QuartzSchedulerListener();
MyQuartzScheduler.ListenerManager.AddSchedulerListener(olistener);
await MyQuartzScheduler.Start();
Related
I know how to cancel a Task, but couldn't find any information on how to add cancellation to ValueTask methods.
Normally I would cancel a Task like this:
public async Task<int> Foo(
CancellationToken cancellationToken)
{
TaskCompletionSource<int> tcsCancel =
new TaskCompletionSource<int>();
cancellationToken.Register(() =>
{
tcsCancel.TrySetCanceled();
});
Task<int> task = LongOperation();
var completedTask = await Task.WhenAny(
tcsCancel.Task,
task).ConfigureAwait(false);
return await completedTask.ConfigureAwait(false);
}
Or like this:
if (cancellationToken.IsCancellationRequested)
return Task.FromCanceled<int>(cancellationToken);
The things is, that ValueTask has neither FromCanceled nor WhenAny. Should I just do ...
cancellationToken.ThrowIfCancellationRequested();
The kind of "cancellation" you're referring to isn't a real cancellation; you're cancelling the wait, not the operation.
You can do the same kind of fake-cancellation with ValueTask<T>; you just need to convert it to Task<T> in order to do it:
public async ValueTask<int> Foo(CancellationToken cancellationToken)
{
using var tcsCancel = new TaskCompletionSource<int>();
using var registration = cancellationToken.Register(() =>
{
tcsCancel.TrySetCanceled();
});
ValueTask<int> valueTask = LongOperation();
Task<int> task = valueTask.AsTask();
var completedTask = await Task.WhenAny(
tcsCancel.Task,
task).ConfigureAwait(false);
return await completedTask.ConfigureAwait(false);
}
.Net 4.5
I have a list of tasks
List<Task> lTasks
each task has its own
'CancellationTokenSource' and 'cancellationToken'
The
Task.Id and Task.status
are listed in a DataGridView.
Each row in the DataGridView also has a button called 'StopTask'.
If user clicked on 'StopTask'button, that specific task should be cancelled based on
Task.Id
How can I do something like that?
Here is my method that creates a new Task every time it is called
private void CreateNewTask(int RowIndex)
{
CancellationTokenSource cts = new CancellationTokenSource(); //Init new CancellationTokenSource
var cancellationToken = cts.Token;
try
{
var t = Task.Factory.StartNew(() =>
{
Console.WriteLine("Start new task");
for (int i = 0; i < 100; i++)
{
dataGridView_TaskList.Rows[RowIndex].Cells["OutputValue1"].Value = i;
System.Threading.Thread.Sleep(1000);
}
cancellationToken.ThrowIfCancellationRequested();
}, cancellationToken);
lTasks.Add(t); //Add to Liast of tasks (List<Task>)
dataGridView_TaskList.Rows[RowIndex].Cells["TaskID"].Value = t.Id;
dataGridView_TaskList.Rows[RowIndex].Cells["TaskStatus"].Value = t.Status;
}
finally
{
cts.Dispose();
}
}
UI with DataGridView
Based on the answers and comments, I did the following and it seems it works fine.
I used DataGridView as my 'Dictionary' and saved the token of each task in
dataGridView_TaskList.Rows[RowIndex].Cells["RowIndex"].Tag = cts;
I also used
RowIndex
as a unique ID, because two tasks may have same TaskID.
New tasks are created as shown below:
private async void CreateNewTask(int RowIndex)
{
CancellationTokenSource cts = new CancellationTokenSource(); //Init new CancellationTokenSource
var cancellationToken = cts.Token;
try
{
Task t = Task.Run(async () => //Task.Run automatically unwraps nested Task types!
{
try
{
Console.WriteLine("Start new task");
for (int i = 0; i < 100; i++)
{
// inside this loop the Token cancellation has no effect, it will keep running until this loop is finished then it will throw a cancellation exception
dataGridView_TaskList.Rows[RowIndex].Cells["OutputValue1"].Value = i;
await Task.Delay(300);
}
cancellationToken.ThrowIfCancellationRequested();
}
catch (OperationCanceledException ex)
{
ShowMsgBox.Show(ex.Message, "Cancelation", enumMessageIcon.Information, enumMessageButton.OK);
}
}, cancellationToken);
lTasks.Add(t); //Add to Liast of tasks (List<Task>)
dataGridView_TaskList.Rows[RowIndex].Cells["TaskID"].Value = t.Id; //The TaskId is not guaranteed to be unique
dataGridView_TaskList.Rows[RowIndex].Cells["TaskStatus"].Value = t.Status;
dataGridView_TaskList.Rows[RowIndex].Cells["RowIndex"].Value = RowIndex;
dataGridView_TaskList.Rows[RowIndex].Cells["RowIndex"].Tag = cts;
}
catch (Exception ex)
{
ShowMsgBox.Show(ex.Message, "Exception", enumMessageIcon.Error, enumMessageButton.OK);
}
}
and task can be cancelled as shown below
private void dataGridView_TaskList_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
var senderGrid = (DataGridView)sender;
if (senderGrid.Columns[e.ColumnIndex] is DataGridViewButtonColumn &&
e.RowIndex >= 0)
{
if (senderGrid.Columns[e.ColumnIndex].Name is "StopTask")
{
//TODO - Button Clicked - Execute Code Here
int.TryParse(dataGridView_TaskList.Rows[e.RowIndex].Cells["RowIndex"].Value.ToString(), out int RowIndex);
CancellationTokenSource cts_ToBeCancelled = (CancellationTokenSource)dataGridView_TaskList.Rows[e.RowIndex].Cells["RowIndex"].Tag;
cts_ToBeCancelled.Cancel();
}
}
}
Add the task ids with cancellation token source to a dictionary, from there you can pull out the cancellation token by id.
Dictionary<int,CancellationTokenSource> taskLookup = new Dictionary<int,CancellationTokenSource>();
...
var t = Task.Factory.StartNew(() =>
...
taskLookup.Add(t.Id, cts);
Also, you can't dispose of the CancellationTokenSource and then use it later to cancel.
You need to call cancellationToken.Cancel(); to cancel a taks.
However, inside your task you need to check whether it was cancelled or not. This probably means that inside your for loop you should add the following:
if (ct.IsCancellationRequested)
{
// Clean up here, then...
ct.ThrowIfCancellationRequested();
}
Note that Thread.Sleep in tasks is a bad idea as this will block on thread from the threadpool. Use await Task.Delay instead
I had the same problem and to resolve it I drived from Task class and added and identifire to MyTask class and in construction injected GUID to distinguish the task.
public class MyTask : Task
{
public Guid Identifire;
public MyTask(Action action, Guid identifire) : base(action)
{
Identifire = identifire;
}
public MyTask(Action action, CancellationToken cancellationToken, Guid identifire) : base(action, cancellationToken)
{
Identifire = identifire;
}
public MyTask(Action action, TaskCreationOptions creationOptions, Guid identifire) : base(action, creationOptions)
{
Identifire = identifire;
}
public MyTask(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions, Guid identifire) : base(action, cancellationToken, creationOptions)
{
Identifire = identifire;
}
public MyTask(Action<object> action, object state, Guid identifire) : base(action, state)
{
Identifire = identifire;
}
public MyTask(Action<object> action, object state, CancellationToken cancellationToken, Guid identifire) : base(action, state, cancellationToken)
{
Identifire = identifire;
}
public MyTask(Action<object> action, object state, TaskCreationOptions creationOptions, Guid identifire) : base(action, state, creationOptions)
{
Identifire = identifire;
}
public MyTask(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions, Guid identifire) : base(action, state, cancellationToken, creationOptions)
{
Identifire = identifire;
}
}
from this sample: here
Using the following queue.
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
public ConcurrentQueue<Func<CancellationToken, Task>> _workItems =
new ConcurrentQueue<Func<CancellationToken, Task>>();
private SemaphoreSlim _signal = new SemaphoreSlim(0);
public void QueueBackgroundWorkItem(
Func<CancellationToken, Task> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
_workItems.Enqueue(workItem);
_signal.Release();
}
public async Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken)
{
await _signal.WaitAsync(cancellationToken);
_workItems.TryDequeue(out var workItem);
return workItem;
}
}
Calling it from within an api controller but need to be able to check on its status.
Controller:
var guid = Guid.NewGuid().ToString();
queue.QueueBackgroundWorkItem(async token=>{
await Task.Delay(TimeSpan.FromSeconds(60), token);
});
How do I associate the guid to the task and check on it later?
This is my situation:
private CancellationTokenSource cancellationTokenSource;
private CancellationToken cancellationToken;
public IoTHub()
{
cancellationTokenSource = new CancellationTokenSource();
cancellationToken = cancellationTokenSource.Token;
receive();
}
private void receive()
{
eventHubClient = EventHubClient.CreateFromConnectionString(connectionString, iotHubD2cEndpoint);
var d2cPartitions = eventHubClient.GetRuntimeInformation().PartitionIds;
foreach (string partition in d2cPartitions)
{
ReceiveMessagesFromDeviceAsync(partition, cancellationToken);
}
}
private async Task ReceiveMessagesFromDeviceAsync(CancellationToken ct)
{
var eventHubReceiver = eventHubClient.GetDefaultConsumerGroup().CreateReceiver(partition, DateTime.UtcNow);
while (true)
{
if(ct.IsCancellationRequested)
{
break;
}
EventData eventData = await eventHubReceiver.ReceiveAsync();
if (eventData == null) continue;
string data = Encoding.UTF8.GetString(eventData.GetBytes());
// Javascript function with Websocket
Clients.All.setMessage(data);
}
}
public void cancelToken()
{
cancellationTokenSource.Cancel();
}
The Task will not be cancelled, when calling the cancelToken method. How come?
I have read the Microsoft guide, an other Stackoverflow questions about Task cancellation.
But still have difficulty using them correctly.
You can consider CancellationToken like a flag, indicating if a cancellation signal is received. Thus:
while (true)
{
//you check the "flag" here, to see if the operation is cancelled, correct usage
if(ct.IsCancellationRequested)
{
break;
}
//your instance of CancellationToken (ct) can't stop this task from running
await LongRunningTask();
}
If you want LongRunningTask to be cancelled, you should use CancellationToken inside the task body and check it when necessary, like this:
async Task LongRunningTask()
{
await DoPrepareWorkAsync();
if (ct.IsCancellationRequested)
{
//it's cancelled!
return;
}
//let's do it
await DoItAsync();
if (ct.IsCancellationRequested)
{
//oh, it's cancelled after we already did something!
//fortunately we have rollback function
await RollbackAsync();
}
}
I wrote down these lines of code:
public static bool RetryUntilSuccessOrTimeoutAsync(Func<bool> task,
TimeSpan executionTimeout, CancellationToken? token = null) {
var data = new ExecutionContextData(task, executionTimeout, token);
var nonBlockingTask = new Task<bool>(SyncTaskExecutor, data);
nonBlockingTask.Start();
var result = nonBlockingTask.ContinueWith(t => t.Result);
return result.Result;
}
class ExecutionContextData {
private readonly Func<bool> task;
private readonly TimeSpan executionTimeout;
private readonly CancellationToken? cancellationToken;
public ExecutionContextData(Func<bool> task, TimeSpan executionTimeout, CancellationToken? cancellationToken) {
this.cancellationToken = cancellationToken;
this.executionTimeout = executionTimeout;
this.task = task;
}
public Func<bool> Task {
get { return task; }
}
public TimeSpan ExecutionTimeout {
get { return executionTimeout; }
}
public CancellationToken? CancellationToken {
get { return cancellationToken; }
}
}
private static bool SyncTaskExecutor(object executionHelper) {
var context = executionHelper as ExecutionContextData;
Task<bool> newTask =
context.CancellationToken.HasValue ? new Task<bool>(ExecuteTask, context.Task, context.CancellationToken.Value)
: new Task<bool>(ExecuteTask, context.Task);
newTask.Start();
bool timeoutResult = newTask.Wait(context.ExecutionTimeout);
if (timeoutResult)
return newTask.Result;
return false;
}
But as I understand the Result property invokation will block a caller. So, I quite don't understand how to accomplish this task:
"How to execute a task asynchronously, so if timeout is exceeded then it will return false or it will return the result of the task which should be executed over and over again?"
Why not try something like this if you have operations that you may want to cancel or have time out:
public static class Retries
{
public enum Result
{
Success,
Timeout,
Canceled,
}
public static Task<Result> RetryUntilTimedOutOrCanceled(this Func<bool> func, CancellationToken cancel, TimeSpan timeOut)
{
return Task.Factory.StartNew(() =>
{
var start = DateTime.UtcNow;
var end = start + timeOut;
while (true)
{
var now = DateTime.UtcNow;
if (end < now)
return Result.Timeout;
var curTimeOut = end - now;
Task<bool> curTask = null;
try
{
if (cancel.IsCancellationRequested)
return Result.Canceled;
curTask = Task.Factory.StartNew(func, cancel);
curTask.Wait((int)curTimeOut.TotalMilliseconds, cancel);
if (curTask.IsCanceled)
return Result.Canceled;
if (curTask.Result == true)
return Result.Success;
}
catch (TimeoutException)
{
return Result.Timeout;
}
catch (TaskCanceledException)
{
return Result.Canceled;
}
catch (OperationCanceledException)
{
return Result.Canceled;
}
}
});
}
}
class Program
{
static void Main(string[] args)
{
var cancelSource = new CancellationTokenSource();
Func<bool> AllwaysFalse = () => false;
Func<bool> AllwaysTrue = () => true;
var result = AllwaysFalse.RetryUntilTimedOutOrCanceled(cancelSource.Token, TimeSpan.FromSeconds(3)).Result;
Console.WriteLine(result);
result = AllwaysTrue.RetryUntilTimedOutOrCanceled(cancelSource.Token, TimeSpan.FromSeconds(3)).Result;
Console.WriteLine(result);
var rTask = AllwaysFalse.RetryUntilTimedOutOrCanceled(cancelSource.Token, TimeSpan.FromSeconds(100));
System.Threading.Thread.Sleep(1000);
cancelSource.Cancel();
result = rTask.Result;
Console.WriteLine(result);
Console.ReadLine();
}
}