I'd like to know how can i check status of an asynchronous task in c#.
I have a save method to save users, and i'd like to run a background task to update them after the save.
I'm in framework 4.0, here is my code to begin the task
System.Threading.Tasks.Task task = null;
task = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
beginTask();
});
My problem is the task take some times to end (near 7 mins) so if someone do several user saves, the task is runned several times, so i'd like to check before running the task if the function beginTask() is already running to avoid to have a lot of background tasks are running.
Thanks
Maybe try do it this way-
Initialise your task object:
Task task = new Task(begintask);
And add new method to run it, for example:
public void StartTask(Task t)
{
if (t.Status == TaskStatus.Running)
return;
else
t.Start();
}
Of course you can add more conditions depend of task's states.
I think I found better solution. Check if it suits you.
public class TaskDemo
{
private static AutoResetEvent autoReset = new AutoResetEvent(true);
Action beginTask = () =>
{
Console.WriteLine("Method start");
Thread.Sleep(2000);
};
public void RunTask()
{
Task myTask = Task.Run(() =>
{
autoReset.WaitOne();
beginTask();
}).ContinueWith(t => autoReset.Set());
}
}
And simply console app test:
static void Main(string[] args)
{
TaskDemo td = new TaskDemo();
// Simulation multiple requests
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
Thread.Sleep(1000);
td.RunTask();
}
The clue is to use AutoResetEvent to signal task state and
Task myTask = Task.Run(() =>
{
autoReset.WaitOne();
beginTask();
}).ContinueWith(t => autoReset.Set());
to change its state during run and after finish (ContinueWith(t => autoReset.Set()).
Each Task object has a Task.Status property of type TaskStatus which can be queried if you already have the task object. It will tell you whether the task is running, finished, cancelled, etc.
If you want to check if ANY task is running that particular chunk of functionality that may be more difficult. One possible suggestion would be keeping a Task variable globally accessible for that reason and any time they tried to run that functionality the code did:
Check the global to see if one is running.
If not create a new Task to run it and assign it to the global variable
Else perform whatever handling you wanted to do if it was already running (wait for it to finish perhaps?)
It sounds like you're a) misunderstanding tasks a bit and b) need to take a look at your solution design.
A task is an object representing a piece of work to be done. A task is NOT the BeginTask method itself. The BeginTask method is more or less just a set of instructions to carry out. It doesn't have any state. Individual Tasks which implement those instructions do have a state which can be queried.
If you want to make it so only one Task could be run per user you'd just have to somewhere globally store a collection of Tasks per user (such as a Dictionary with the Key being the user).
This would ideally be created and stored in either some sort of governing class that contains this section of application functionality or in the outer program if it is one.
To make reference to your comment of "i need to avoid to create a new task everytime i save my users", for this you're going to have to adapt that particular piece of code to check the stored status of any running Tasks. So in my idea above you'd alter that piece of functionality to check if a Task exists for the user you're saving in the Dictionary of already begun tasks and if it does, check the status of it.
If you're not sure though please keep asking questions in the comments. Perhaps if you gave more information on how the system this is in, is structured I'd be better able to assist.
Hope that helps.
You can use property Task.IsCompleted or Task.Status
And by the way try to investigation of this method Task.ContinueWith
I think using of Task.ContinueWith is better way for multitask solutions.
See example below which explain my suggestion with using Task.ContinueWith:
System.Threading.Tasks.Task task = null;
if (task==null)
{
task = Task.Factory.StartNew(() =>
{
beginTask();
});
return;
}
if (task.Status == TaskStatus.Running)
{
task.ContinueWith((x) =>
{
beginTask();
});
}
else
{
task = Task.Factory.StartNew(() =>
{
beginTask();
});
}
This implementation can resolve scope of potential problem like:
How to keep previous Task
When should system launch previous Tasks
Related
I need your help, with threads I'm full 0 and you only need to create a certain thread and complete it on command, BUT I do not create each thread in advance, as there will be a lot of them, I do it like this:
Thread thread = new Thread(() => Go(..... many many variables that are taken from the listview ......));
thread.Start();
So, as noted above, variables are taken from the listview, which in turn is loaded by me from the file and then I run the threads I need. BUT the process in the stream is infinite and will end only if I completely close the program, and I would like to end the stream in the same way as I started it (right click on the desired line-start/stop). As I said, I have never worked with threads and thought that it was somehow simple, like when you start a thread, you assign it an ID and end it with the same ID, but alas. I have searched all over Google and have not found an EXAMPLE that suits me (I will repeat for the third time - I have never worked with threads and I do not need to say "go read about TPL"), so I ask for help, preferably with an example)
I have a very bad idea: in the sheet there is an invisible column in which an id is generated at the start, then when I send a command to start the thread, a unique variable is created with the name for example int id1=0 and its name is passed to the thread itself and each time the loop starts, id1=0 or 1 is checked in it, respectively, if 0-continue, if 1-empty. Well, it is logical that when you click the stop button, its value changes to 1. But something seems to me that the holy spirit of multithreading will punish me for this when the threads become 100+. I read this idea somewhere, so don't swear)
You do not need hundreds of threads for this. Your worker "threads" are performing HTTP requests, which can be done asynchronously without requiring a new thread. Also, hundreds of threads wouldn't really help you unless you have hundreds of CPU cores (you don't).
For this sort of work, I'd recommend the following:
Write a method that does all the work your thread does, but also checks a CancellationToken with each iteration.
Calls the method in a loop, once for each account, and store the resulting tasks in an array or list. Or use LINQ (as I do in this example) to create the list.
When your program terminates, activate the CancellationToken.
After cancelling, you have to await all the tasks in order to observe any possible exceptions and exit cleanly.
For example
public async Task DoTheWork(Account account, CancellationToken token)
{
while (!token.IsCancellationRequested)
{
var result = await httpClient.GetAsync(account.Url);
await DoSomethingWithResult(result);
await Task.Delay(1000);
}
}
//Main program
var accounts = GetAccountList();
var source = new CancellationTokenSource();
var tasks = accounts.Select( x => DoTheWork(x, source.Token) ).ToList();
//When exiting
source.Cancel();
await Task.WhenAll( tasks );
source.Dispose();
Indivivdual cancellation
Here's another approach that keeps a list of the accounts and a delegate that can be used for cancelling the task for that specific account.
//Declare this somewhere it will persist for the duration of the program
//The key to this dictionary is the account you wish to cancel
//The value is a delegate that you can call to cancel its task
Dictionary<Account, Func<Task>> _tasks = new Dictionary<Account, Func<Task>>();
async Task CreateTasks()
{
var accounts = GetAccounts();
foreach (var account in accounts)
{
var source = new CancellationTokenSource();
var task = DoTheWork(account, source.Token);
_tasks.Add(account, () => { source.Cancel(); return task; });
}
}
//Retrieve the delegate from the dictionary and call it to cancel its task
//Then await the task to observe any exceptions
//Then remove it from the list
async Task CancelTask(Account account)
{
var cancelAction = _tasks[account];
var task = cancelAction();
await task;
_tasks.Remove(account);
}
async Task CancelAllTasks()
{
var tasks = _tasks.Select(x => x.Value()).ToList();
await Task.WhenAll(tasks);
}
In my application I have the need to continually process some piece(s) of Work on some set interval(s). I had originally written a Task to continually check a given Task.Delay to see if it was completed, if so the Work would be processed that corresponded to that Task.Delay. The draw back to this method is the Task that checks these Task.Delays would be in a psuedo-infinite loop when no Task.Delay is completed.
To solve this problem I found that I could create a "recursive Task" (I am not sure what the jargon for this would be) that processes the work at the given interval as needed.
// New Recurring Work can be added by simply creating
// the Task below and adding an entry into this Dictionary.
// Recurring Work can be removed/stopped by looking
// it up in this Dictionary and calling its CTS.Cancel method.
private readonly object _LockRecurWork = new object();
private Dictionary<Work, Tuple<Task, CancellationTokenSource> RecurringWork { get; set; }
...
private Task CreateRecurringWorkTask(Work workToDo, CancellationTokenSource taskTokenSource)
{
return Task.Run(async () =>
{
// Do the Work, then wait the prescribed amount of time before doing it again
DoWork(workToDo);
await Task.Delay(workToDo.RecurRate, taskTokenSource.Token);
// If this Work's CancellationTokenSource is not
// cancelled then "schedule" the next Work execution
if (!taskTokenSource.IsCancellationRequested)
{
lock(_LockRecurWork)
{
RecurringWork[workToDo] = new Tuple<Task, CancellationTokenSource>
(CreateRecurringWorkTask(workToDo, taskTokenSource), taskTokenSource);
}
}
}, taskTokenSource.Token);
}
Should/Could this be represented with a chain of Task.ContinueWith? Would there be any benefit to such an implementation? Is there anything majorly wrong with the current implementation?
Yes!
Calling ContinueWith tells the Task to call your code as soon as it finishes. This is far faster than manually polling it.
I am currently using tasks in some projects, and I was wondering how can I actually run a parallel code multiple times with the same method.
Here is my code:
Task CPU1 = new Task(CPU1_DoWork);
Task CPU2 = new Task(CPU2_DoWork);
// And i wanted to execute the CPU1 Task multiple time here is how i used it:
while (Timer_General.IsRunning == true)
{
if (CPU1.Status != TaskStatus.Running)
{
CPU1.Start();
}
if (CPU2.Status != TaskStatus.Running)
{
CPU2.Start();
}
}
// Other Blabla
Why is he running CPU1_DoWork only once? Thanks for your help guys! :)
I added the CPU2 sequence because if we await the first, the second will not get called :/ So this poses a problem. Is there any other way to make my Tasks run multiple time at the same time?
A Task cannot be re-used. For each one you want to use you'll need to create a new instance. Instead of using the Task, you should use the built in factory method Task.Run which will return a hot task for you:
public async Task FooAsync()
{
while (Timer_General.IsRunning)
{
await Task.Run(CPU1_DoWork);
}
}
As you're checking for Task.IsRunning, I'm assuming you want to asynchronously wait for each task to complete, hence I added the await keyword.
Edit:
Since you added a second task and want them botg to run in parallel, you can use Task.WhenAll:
public async Task FooAsync()
{
while (Timer_General.IsRunning)
{
await Task.WhenAll(Task.Run(CPU1_DoWork),
Task.Run(CPU2_Work));
}
}
I'm switching from Task.Run to Hangfire. In .NET 4.5+ Task.Run can return Task<TResult> which allows me to run tasks that return other than void. I can normally wait and get the result of my task by accessing the property MyReturnedTask.Result
Example of my old code:
public void MyMainCode()
{
List<string> listStr = new List<string>();
listStr.Add("Bob");
listStr.Add("Kate");
listStr.Add("Yaz");
List<Task<string>> listTasks = new List<Task<string>>();
foreach(string str in listStr)
{
Task<string> returnedTask = Task.Run(() => GetMyString(str));
listTasks.Add(returnedTask);
}
foreach(Task<string> task in listTasks)
{
// using task.Result will cause the code to wait for the task if not yet finished.
// Alternatively, you can use Task.WaitAll(listTasks.ToArray()) to wait for all tasks in the list to finish.
MyTextBox.Text += task.Result + Environment.NewLine;
}
}
private string GetMyString(string str)
{
// long execution in order to calculate the returned string
return str + "_finished";
}
As far as I can see from the Quick Start page of Hangfire, your main guy which is BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget"));
perfectly runs the code as a background job but apparently doesn't support jobs that have a return value (like the code I presented above). Is that right? if not, how can I tweak my code in order to use Hangfire?
P.S. I already looked at HostingEnvironment.QueueBackgroundWorkItem (here) but it apparently lacks the same functionality (background jobs have to be void)
EDIT
As #Dejan figured out, the main reason I want to switch to Hangfire is the same reason the .NET folks added QueueBackgroundWorkItem in .NET 4.5.2. And that reason is well described in Scott Hanselman's great article about Background Tasks in ASP.NET. So I'm gonna quote from the article:
QBWI (QueueBackgroundWorkItem) schedules a task which can run in the background, independent of
any request. This differs from a normal ThreadPool work item in that
ASP.NET automatically keeps track of how many work items registered
through this API are currently running, and the ASP.NET runtime will
try to delay AppDomain shutdown until these work items have finished
executing.
One simple solution would be to poll the monitoring API until the job is finished like this:
public static Task Enqueue(Expression<Action> methodCall)
{
string jobId = BackgroundJob.Enqueue(methodCall);
Task checkJobState = Task.Factory.StartNew(() =>
{
while (true)
{
IMonitoringApi monitoringApi = JobStorage.Current.GetMonitoringApi();
JobDetailsDto jobDetails = monitoringApi.JobDetails(jobId);
string currentState = jobDetails.History[0].StateName;
if (currentState != "Enqueued" && currentState != "Processing")
{
break;
}
Thread.Sleep(100); // adjust to a coarse enough value for your scenario
}
});
return checkJobState;
}
Attention: Of course, in a Web-hosted scenario you cannot rely on continuation of the task (task.ContinueWith()) to do more things after the job has finished as the AppDomain might be shut down - for the same reasons you probably want to use Hangfire in the first place.
I have some code that creates a task that does some slow work like this:
public static Task wait1()
{
return new Task(() =>
{
Console.WriteLine("Waiting...");
Thread.Sleep(10000);
Console.WriteLine("Done!");
});
}
In the real implementation, the Thread.Sleep will actually be a web service call. I would like to change the body of the method can use await (so it does not consume a thread during the network access/sleep). My first attempt (based on shotgun-debugging the compile errors) was this:
public static Task wait2()
{
return new Task(async () =>
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
});
}
However; this task doesn't seem to behave the same as the first one, because when I call .Wait() on it; it returns immediately.
Below is a full sample (console app) showing the differences (the app will end immediately when the second task starts).
What do I need to do so that I can call Start and Wait on a Task which happens to have code using await inside it? The tasks are queued and executed later by an agent, so it's vital that the task is not auto-started.
class Program
{
static void Main(string[] args)
{
var w1 = wait1();
w1.Start();
w1.Wait(); // This waits 110 seconds
var w2 = wait2();
w2.Start();
w2.Wait(); // This returns immediately
}
public static Task wait1()
{
return new Task(() =>
{
Console.WriteLine("Waiting...");
Thread.Sleep(10000);
Console.WriteLine("Done!");
});
}
public static Task wait2()
{
return new Task(async () =>
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
});
}
}
It seems like this isn't possible! See alexm's answer here:
Tasks returned by async methods are always hot i.e. they are created in Running state.
:-(
I've worked around this by making my agent queue Func<Task>s instead, and the overload that receives a task simply queues () => task. Then; when de-queing a task, I check if it's not running, and if so, start it:
var currentTask = currentTaskFunction();
if (currentTask.Status == TaskStatus.Created)
currentTask.Start();
It seems a little clunky to have to do this (if this simple workaround works; why the original restriction on async methods always being created hot?), but it seems to work for me :-)
You could write this as:
public static async Task Wait2()
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
}
In general, it's rarely a good idea to ever use new Task or new Task<T>. If you must launch a task using the ThreadPool instead of using the async/await language support to compose one, you should use Task.Run to start the task. This will schedule the task to run (which is important, tasks should always be "hot" by conventions).
Note that doing this will make it so you don't have to call Task.Start, as well.
To help you understand this realize that async / await essentially does not create a new thread but rather it schedules that portion of code to be ran at an available point in time.
When you create the new Task(async () => ...) you have a task that run an async method. When that inner async method hits an await the 'new Task' is considered complete because the rest of it has been scheduled. To help you understand better place some code (a lot if wanted) in the 'new Task' before the await command. It will all execute before the application terminates and once await is reached that task will believe it has completed. It then returns and exits the application.
The best way to avoid this is to not place any task or async methods inside of your task.
Remove the async keyword and the await keyword from the method and it will work as expected.
This is the same as creating a callback if you're familiar with that.
void MethodAsync(Action callback)
{
//...some code
callback?.Invoke();
}
//using this looks like this.
MethodAsync(() => { /*code to run when complete */});
//This is the same as
Task MethodAsync()
{
//... some code here
}
//using it
await MethodAsync();
/*code to run when complete */
The thing to understand is that you're creating a new task within a task basically. So the inner 'callback' is being created at the await keyword.
You're code looks like this..
void MethodAsync(Action callback)
{
//some code to run
callback?.Invoke(); // <- this is the await keyword
//more code to run.. which happens after we run whoever is
//waiting on callback
}
There's code missing obviously. If this doesn't make sense please feel free to contact me and I'll assist. async / await (meant to make things simpler) is a beast to wrap your head around at first. Afterward you get it then it'll probably be your favorite thing in c# since linq. :P
Try this:
public async static Task wait2()
{
Console.WriteLine("Waiting...");
await Task.Delay(2000);
Console.WriteLine("Done!");
}
But we aware that the task is already started so you don't have to call start:
var w2 = wait2();
//w2.Start();
w2.Wait();
I think the problem with your wait2 function is that is creating 2 task, the one in new Task(...) and another in Task.Delay(). You are waiting for the first one, but you are not waiting for the inner one.