Maybe my brain is a bit fried so I'm missing some nice way to do the following... I want to be able to launch a timer though a Task that runs on a certain interval and checks some condition on each interval whether it should cancel itself, what's the most elegant solution?
Optimally I'd like something like:
Task.Factory.StartNew(() =>
{
Timer.Do(TimeSpan.FromMilliSeconds(200),() => ShouldCancel(), ()=>
{
//DoStuff
});
});
using a while/thread-sleep loop doesn't seem optimal. I guess I could define and use a ordinary timer but it seems a bit clunky...
How about something like the following.I'm sure the API could be cleaned up a bit.
Points to note:
The DoWork method must support cooperative cancellation, this is the only cancellation approach supported by the Task Parallel Library.
The timer must start inside the Task, otherwise the Task may be created and scheduled but not executed and the timer will be timing task wait time not execution time.
If you want to provide other external mechanisms for cancellation (other tokens) then you need to pass in another context and link them. See: CancellationTokenSource.CreateLinkedTokenSource
This is only approximate as System.Threading.Timer only has millisecond accuracy. It should be good enough for limiting a Task to run for a few seconds.
public static class TimeLimitedTaskFactory
{
public static Task StartNew<T>
(Action<CancellationToken> action, int maxTime)
{
Task tsk = Task.Factory.StartNew(() =>
{
var cts = new CancellationTokenSource();
System.Threading.Timer timer = new System.Threading.Timer(o =>
{
cts.Cancel();
Console.WriteLine("Cancelled!");
}, null, maxTime, int.MaxValue);
action(cts.Token);
});
return tsk;
}
}
class Program
{
static void Main(string[] args)
{
int maxTime = 2000;
int maxWork = 10;
Task tsk = TimeLimitedTaskFactory
.StartNew<int>((ctx) => DoWork(ctx, maxWork), maxTime);
Console.WriteLine("Waiting on Task...");
tsk.Wait();
Console.WriteLine("Finished...");
Console.ReadKey();
}
static void DoWork(CancellationToken ctx, int workSize)
{
int i = 0;
while (!ctx.IsCancellationRequested && i < workSize)
{
Thread.Sleep(500);
Console.WriteLine(" Working on ", ++i);
}
}
}
You also can use RX library.
var timerTask = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(3));
timerTask.Subscribe(x =>
{
//Do stuff here
});
I think this is what you want:
var cancelToken = new CancellationTokenSource();
var tt = Task.Factory.StartNew(obj =>
{
var tk = (CancellationTokenSource) obj;
while (!tk.IsCancellationRequested)
{
if (condition)//your condition
{
//Do work
}
Thread.Sleep(1000);
}
}, cancelToken);
Related
I've got multiple System.Threading.Timer which starts in parallel. In the end, I have got a Task.Wait to wait till all tasks are done. But it doesn't wait for all, how can I make it wait for all?
private List<Task> todayTasks = new List<Task>();
foreach (var item in todayReport)
{
todayTasks.Add(SetupTimer(item.Exec_Time, item.Report_Id));
}
Task.WaitAll(todayTasks.ToArray());
--SetupTimer--
private Task SetupTimer(DateTime alertTime, int id)
{
DateTime current = DateTime.Now;
TimeSpan timeToGo = alertTime.TimeOfDay - current.TimeOfDay;
if (timeToGo < TimeSpan.Zero) {
//TODO: ERROR time already passed
}
ExecCustomReportService executeCustom = new ExecCustomReportService();
return Task.Run(
() => new Timer(
x => executeCustom.AdhockReport(id), null, timeToGo, Timeout.InfiniteTimeSpan
)
);
}
You're really better off using a tool that is suited to the job. I'd suggest Microsoft's Reactive Framework (Rx). Then you can do this:
var query =
from item in todayReport.ToObservable()
from report in Observable.Start(() => executeCustom.AdhockReport(item.Report_Id))
select report;
IDisposable subscription =
query
.Subscribe(
report =>
{
/* Do something with each report */
},
() =>
{
/* Do something when finished */
});
You just need to NuGet "System.Reactive".
As #YacoubMassad stated in the comment, your task is merely creating the timer and returns.
What you could do is get rid of the timer and use Task.Delay:
return Task.Delay(timeToGo).ContinueWith(t=> executeCustom.AdhockReport(id));
I currently am learning how to use Tasks in c#, i want to be able to run 2 tasks at the same time. then when the first task ends. tell the code to stop the second one. I have tried many things but none have worked, i have tried:
Try looking for something related to task.stop and have not found it. i am using task.wait for the first task so when the first one ends i have to do something to stop the second one.
Since the second one is infinite (its an eternal loop) i tried making the parameter of the loop something i could change in the main code, but its like the task is a method and variables in them are unique.
TL;DR: I want to know if i can change a parameter inside a task in order to stop it from outside its code. do the task itself take any parameters? and can i change them in the main code after they start running?
If none of the previous things are possible is it then possible in any way to stop an infinite task?
CODE:
Task a = new Task(() =>
{
int sd = 3;
while (sd < 20)
{
Console.Write("peanuts");
sd++; //this i can change cuz its like local to the task
}
});
a.Start();
// infinite task
Task b = new Task(() =>
{
int s = 3; // parameter i want to change to stop it
while (s < 10)
{
Console.Write(s+1);
}
});
b.Start();
a.Wait();
// Now here I want to stop task b
Console.WriteLine("peanuts");
Console.ReadKey();
Try this:
public static void Run()
{
CancellationTokenSource cts = new CancellationTokenSource();
Task1(cts);
Task2(cts.Token);
}
private static void Task2(CancellationToken token)
{
Task.Factory.StartNew(() =>
{
int s = 3; // parameter i want to change to stop it
while (!token.IsCancellationRequested)
{
Console.Write(s + 1);
}
}, token);
}
private static void Task1(CancellationTokenSource cts)
{
Task.Factory.StartNew(() =>
{
int sd = 3;
while (sd < 20)
{
Console.Write("peanuts");
sd++; //this i can change cuz its like local to the task
}
}).ContinueWith(t => cts.Cancel());
}
CancellationTokenSource will be cancelled when Task1 is finished. So, Task2 checks cancellation token each iteration and exits infinite loop when cancellation is requested.
i'm trying to build a list of tasks before executing them. here's some example code:
public string Returnastring(string b)
{
return b;
}
public string Returnanotherstring(string a)
{
return a;
}
private void btnT_Click(object sender, EventArgs e)
{
bool cont = true;
var Returnastringtask = Task.Factory.StartNew(() => Returnastring("hi"));
var Returnanotherstringtask = Task.Factory.StartNew(() => Returnanotherstring("bye"));
if (cont)
{
Task.WaitAll(new Task[] { Returnastringtask });
}
else
{
Task.WaitAll(new Task[] { Returnanotherstringtask });
}
i know this code doesn't behave how i expect it as both tasks run. i want to basically create the tasks initially and then execute one or the other based on the bool. i don't want to create the tasks inside the true or false conditions as i want to avoid code copying. by this i mean if cont is true i might want to run tasks 1,2,3,4 but if cont is false i might want to run tasks 2,3,7,8.
Well, another approach, (which I find very direct)
var list = new List<Task>();
for (var i = 0; i < 10; ++i)
{
var i2 = i;
var t = new Task(() =>
{
Thread.Sleep(100);
Console.WriteLine(i2);
});
list.Add(t);
t.Start();
}
Task.WaitAll(list.ToArray());
Instead of using Task.Factory.StartNew to create the tasks (the clue is in the name), instead just create them by using new Task(...) with your lambdas, then simply use taskName.Start() inside the condition you want to begin them.
You can create an array of Action based on a flag, and then use Parallel.Invoke() to run in parallel all the actions in the array and wait for them to finish.
You can use lambdas for the actions which will allow you to assign their return values to a local variable, if you want.
Here's a complete compilable example. Try it with getFlag() returning true and again with it returning false:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
sealed class Program
{
void run()
{
bool flag = getFlag();
var results = new string[5];
Action[] actions;
if (flag)
{
actions = new Action[]
{
() => results[0] = function("f1"),
() => results[1] = function("f2"),
() => results[2] = function("f3")
};
}
else
{
actions = new Action[]
{
() => results[3] = function("f4"),
() => results[4] = function("f5")
};
}
Parallel.Invoke(actions); // No tasks are run until you call this.
for (int i = 0; i < results.Length; ++i)
Console.WriteLine("Result {0} = {1}", i, results[i]);
}
private bool getFlag()
{
return true; // Also try with this returning false.
}
string function(string param)
{
Thread.Sleep(100); // Simulate work.
return param;
}
static void Main(string[] args)
{
new Program().run();
}
}
}
The Task.Factory.StartNew will actually begin your tasks. You want to setup the tasks and then run them based on some logic.
You can build your tasks wherever but you should start them after the logic. This example builds them after the logic.
Maybe you could run it like this:
If(A)
{
doA();
}
Else
{
doB();
}
Then start your tasks inside the function you call like:
public void doA()
{
for (int i = 0; i < NumberOfTasks; i++)
{
tasks[i] = Task.Factory.StartNew(() =>
{
try
{
//enter tasks here
// i.e. task 1, 2, 3, 4
}
}
}, token);
Task.WaitAll(tasks);
}
I based what I did on what Samuel did, except I have a recursive event handler that needs to finish what it's doing because its child events depend on it having completed (for nesting controls in a dynamic UI in an ASP.NET app). So if you want to do the same thing, except you're handling an event, and you are NOT multithreading because you need to process multiple tasks synchronously without goofing around with your call stack.
private static Queue<Task> _dqEvents = new Queue<Task>();
private static bool _handlingDqEvent = false;
protected void HandleDynamicQuestion(int SourceQuestionId, int QuestionId)
{
//create a task so that we can handle our events in sequential order, since additional events may fire before this task is completed, and depend upon the completion of prior events
Task task = new Task(() => DoDynamicQuestion(SourceQuestionId, QuestionId));
lock(_dqEvents) _dqEvents.Enqueue(task);
if (!_handlingDqEvent)
{
try
{
//lockout any other calls in the stack from hitting this chunk of code
lock (_dqEvents) _handlingDqEvent = true;
//now run all events in the queue, including any added deeper in the call stack that were added to this queue before we finished this iteration of the loop
while (_dqEvents.Any())
{
Task qt;
lock (_dqEvents) qt = _dqEvents.Dequeue();
qt.RunSynchronously();
}
}
finally
{
lock (_dqEvents) _handlingDqEvent = false;
}
}
else
//We exit the method if we're already handling an event, as the addition of new tasks to the static queue will be handled synchronously.
//Basically, this lets us escape the call stack without processing the event until we're ready, since the handling of the grandchild event
//is dependent upon its parent completing.
return;
}
private void DoDynamicQuestion(int SourceQuestionId, int QuestionId)
{
//does some stuff that has no dependency on synchronicity
//does some stuff that may eventually raise the event above
//does some other stuff that has to complete before events it triggers can process correctly
}
I recently came across a case where it would be handy to be able to spawn a bunch of threads, block and wait for exactly one answer (the first one to arrive), cancelling the rest of the threads and then unblocking.
For example, suppose I have a search function that takes a seed value. Let us stipulate that the search function can be trivially parallelized. Furthermore, our search space contains many potential solutions, and that for some seed values, the function will search indefinitely, but that at least one seed value will yield a solution in a reasonable amount of time.
It would be great if I could to this search in parallel, totally naively, like:
let seeds = [|0..100|]
Array.Parallel.map(fun seed -> Search(seed)) seeds
Sadly, Array.Parallel.map will block until all of the threads have completed. Bummer. I could always set a timeout in the search function, but then I'm almost certain to wait for the longest-running thread to finish; furthermore, for some problems, the timeout might not be long enough.
In short, I'd like something sort of like the UNIX sockets select() call, only for arbitrary functions. Is this possible? It doesn't have to be in a pretty data-parallel abstraction, as above, and it doesn't have to be F# code, either. I'd even be happy to use a native library and call it via P/Invoke.
You can create a bunch of tasks and then use Task.WaitAny or Task.WhenAny to either synchronously wait for the first task to finish or create a task that will be completed when the first task finishes, respectively.
A simple synchronous example:
var tasks = new List<Task<int>>();
var cts = new CancellationTokenSource();
for (int i = 0; i < 10; i++)
{
int temp = i;
tasks.Add(Task.Run(() =>
{
//placeholder for real work of variable time
Thread.Sleep(1000 * temp);
return i;
}, cts.Token));
}
var value = Task.WaitAny(tasks.ToArray());
cts.Cancel();
Or for an asynchronous version:
public static async Task<int> Foo()
{
var tasks = new List<Task<int>>();
var cts = new CancellationTokenSource();
for (int i = 0; i < 10; i++)
{
int temp = i;
tasks.Add(Task.Run(async () =>
{
await Task.Delay(1000 * temp, cts.Token);
return temp;
}));
}
var value = await await Task.WhenAny(tasks);
cts.Cancel();
return value;
}
let rnd = System.Random()
let search seed = async {
let t = rnd.Next(10000)
//printfn "seed: %d ms: %d" seed t
do! Async.Sleep t
return sprintf "seed %d finish" seed
}
let processResult result = async {
//Todo:
printfn "%s" result
}
let cts = new System.Threading.CancellationTokenSource()
let ignoreFun _ = () //if you don't want handle
let tasks =
[0..10]
|> List.map (fun i ->
async {
let! result = search i
do! processResult result
cts.Cancel()
}
)
Async.StartWithContinuations(Async.Parallel tasks, ignoreFun, ignoreFun, ignoreFun, cts.Token)
Try synchronizng all threads using an event object, when you find a solution set the event, all others threads have to check periodically for the event state and stop execution if it was already set.
For more details, look here.
This seemed to work for me
namespace CancellParallelLoops
{
class Program
{
static void Main(string[] args)
{
int[] nums = Enumerable.Range(0, 10000000).ToArray();
CancellationTokenSource cts = new CancellationTokenSource();
// Use ParallelOptions instance to store the CancellationToken
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;
po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
Console.WriteLine("Press any key to start. Press 'c' to cancel.");
Console.ReadKey();
// Run a task so that we can cancel from another thread.
Task.Factory.StartNew(() =>
{
if (Console.ReadKey().KeyChar == 'c')
cts.Cancel();
Console.WriteLine("press any key to exit");
});
try
{
Parallel.ForEach(nums, po, (num) =>
{
double d = Math.Sqrt(num);
Console.WriteLine("{0} on {1}", d, Thread.CurrentThread.ManagedThreadId);
if (num == 1000) cts.Cancel();
po.CancellationToken.ThrowIfCancellationRequested();
});
}
catch (OperationCanceledException e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
}
}
I have a Windows Service which starts a task on start up
This task which has a while loop and after performing one iteration it go to sleep for 5 minutes.
When I stop service, the task is cancelled first and later some other operations gets performed
if the task is in sleep, it get cancelled only when it wakes up , i want it to be cancelled even if it is sleeping and don't want to wait for waking it up.
following is the code
Task controllerTask = Task.Factory.StartNew(() =>
{
var interval = 300;
while(true)
{
if (cancellationToken.IsCancellationRequested)
break;
Thread.Sleep(interval * 1000);
if (cancellationToken.IsCancellationRequested)
break;
//SOME WORK HERE
}
}, cancellationToken);
Is there any way?
EDIT:
I am not able to use Task.Delay , I can't find it in System.Threading.Tasks.Task namespace , because I am using .Net Framework 4.0 not 4.5
Is there any other better solution that works with 4.0.
Use Task.Delay instead of Thread.Sleep. It takes a CancellationToken parameter so you can abort it before the end of the delay.
If you're using async code, you could write it like this:
await Task.Delay(duration, cancellationToken);
If it's synchronous code, you can just wait the task:
Task.Delay(duration, cancellationToken).Wait();
This is one blocking solution you can use in C# 4.0, VS2010.
cancellationToken.WaitHandle.WaitOne(TimeSpan.FromMinutes(5));
It will unblock when you cancel the token source or on timeout which is your desired sleep interval.
Inspired by the other answers, simple example of using await for this problem:
public static class TaskExtension
{
/// <summary>
/// Call to highlight fact that you do not want to wait on this task.
///
/// This nicely removes resharper warnings without need for comments.
/// </summary>
/// <param name="task"></param>
public static void FireAndForget(this Task task)
{
}
}
internal class Program
{
private static void Main(string[] args)
{
var cancellationToken = new CancellationTokenSource();
TaskCode(cancellationToken.Token).FireAndForget();
Console.ReadLine();
cancellationToken.Cancel();
Console.WriteLine("End");
Console.ReadLine();
}
private static async Task TaskCode(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var interval = TimeSpan.FromSeconds(1);
await Task.Delay(interval, cancellationToken);
//SOME WORK HERE
Console.WriteLine("Tick");
}
}
}
I've broken long sleep into multiple small sleeps, following is the modified code:
Task controllerTask = Task.Factory.StartNew(() =>
{
while(true)
{
if (cancellationToken.IsCancellationRequested) break;
var sleepTime = 10;
if (interval < sleepTime)
interval = sleepTime;
var iteration = (interval / sleepTime);
if ((interval % sleepTime) > 0)
iteration++;
bool cancel = false;
for (int i = 0; i < iteration; i++)
{
Thread.Sleep(sleepTime * 1000);
if (cancellationToken.IsCancellationRequested) { cancel = true; break; };
}
if (cancel) break;
//SOME WORK HERE
}
}