Multithreading- Function exits before thread finishes. C# WPF - c#

I have a function that generates database layers for a map application but since the computation takes too long I put it into a thread. However, obviously, but not to me 5 seconds ago, the function returns without it finishing the computation so the function returns nothing. Is there any way I can still have the work be sent to where it needs to go?
public static int calcNum()
{
int value = 0;
Thread thread1 = new Thread(() => {
value = 5;
Thread.Sleep(5000); //Simulates work being done
});
return value; //Returns 0 when I want it to be 5
}

A "computation" doesn't get any faster at all just because you run it on a different thread.
If don't want your method to return before the computation has finished, you should implement your method like this without involving any additional thread:
public static int calcNum()
{
int value = 0;
value = 5;
Thread.Sleep(5000); //Simulates work being done
return value;
}
If you want to prevent the calling thread from being blocked while executing the "computation", you could implemenent an async method that executes the long running operation in a Task:
public static async Task<int> CalcNumAsync()
{
int value = 0;
await Task.Run(() =>
{
value = 5;
Thread.Sleep(5000); //Simulates work being done
});
return value;
}
You will then have to await the method when you call it:
int num = await CalcNumAsync();
The latter approach is for example useful in UI application, when you want the applicaiton to stay responsive while your long running operation is being executed on a background thread.

Related

Return data from long running Task on demand

I want to create a Task, which may run for many minutes, collecting data via an API call to another system. At some point in the future I need to stop the task and return the collected data. This future point is unknown at the time of starting the task.
I have read many question about returning data from tasks, but I can't find any that answer this scenario. I may be missing a trick, but all of the examples actually seem to wait in the man thread for the task to finish before continuing. This seems counter-intuitive, surely the purpose of a task is to hand off an activity whilst continuing with other activities in your main thread?
Here is one of those many examples, taken from DotNetPearls..
namespace TaskBasedAsynchronousProgramming
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Main Thread Started");
Task<double> task1 = Task.Run(() =>
{
return CalculateSum(10);
});
Console.WriteLine($"Sum is: {task1.Result}");
Console.WriteLine($"Main Thread Completed");
Console.ReadKey();
}
static double CalculateSum(int num)
{
double sum = 0;
for (int count = 1; count <= num; count++)
{
sum += count;
}
return sum;
}
}
}
Is it possible to do what I need, and have a long-running task running in parallel, stop it and return the data at an arbitrary future point?
Here is a sample application how you can do that:
static double partialResult = -1;
static void Main()
{
CancellationTokenSource calculationEndSignal = new(TimeSpan.FromSeconds(3));
Task meaningOfLife = Task.Run(() =>
GetTheMeaningOfLife(calculationEndSignal.Token),
calculationEndSignal.Token);
calculationEndSignal.Token.Register(() => Console.WriteLine(partialResult));
Console.ReadLine();
}
static async Task GetTheMeaningOfLife(CancellationToken cancellationToken)
{
foreach (var semiResult in Enumerable.Range(1, 42))
{
partialResult = semiResult;
cancellationToken.ThrowIfCancellationRequested();
await Task.Delay(1000);
}
}
partialResult is a shared variable between the two threads
The worker thread (GetTheMeaningOfLife) only writes it
The main thread (Main) only reads it
The read operation is performed only after the Task has been cancelled
calculationEndSignal is used to cancel the long-running operation
I've have specified a timeout, but you can call the Cancel method if you want
meaningOfLife is the Task which represents the long-running operation call
I have passed the CancellationToken to the GetTheMeaningOfLife and to the Task.Run as well
For this very simple example the Task.Run should not need to receive the token but it is generally a good practice to pass there as well
Register is receiving a callback which should be called after the token is cancelled
ReadLine can be any other computation
I've used ReadLine to keep the application running
GetTheMeaningOfLife simply increments the partialResult shared variable
either until it reaches the meaning of life
or until it is cancelled
Here is one approach. It features a CancellationTokenSource that is used as a stopping mechanism, instead of its normal usage as a cancellation mechanism. That's because you want to get the partial results, and a canceled Task does not propagate results:
CancellationTokenSource stoppingTokenSource = new();
Task<List<int>> longRunningTask = Task.Run(() =>
{
List<int> list = new();
for (int i = 1; i <= 60; i++)
{
if (stoppingTokenSource.IsCancellationRequested) break;
// Simulate a synchronous operation that has 1 second duration.
Thread.Sleep(1000);
list.Add(i);
}
return list;
});
Then, somewhere else in your program, you can send a stopping signal to the task, and then await asynchronously until the task acknowledges the signal and completes successfully. The await will also propagate the partial results:
stoppingTokenSource.Cancel();
List<int> partialResults = await longRunningTask;
Or, if you are not in an asynchronous workflow, you can wait synchronously until the partial results are available:
stoppingTokenSource.Cancel();
List<int> partialResults = longRunningTask.Result;

Random tasks from Task.Factory.StartNew never finishes

I am using Async await with Task.Factory method.
public async Task<JobDto> ProcessJob(JobDto jobTask)
{
try
{
var T = Task.Factory.StartNew(() =>
{
JobWorker jobWorker = new JobWorker();
jobWorker.Execute(jobTask);
});
await T;
}
This method I am calling inside a loop like this
for(int i=0; i < jobList.Count(); i++)
{
tasks[i] = ProcessJob(jobList[i]);
}
What I notice is that new tasks opens up inside Process explorer and they also start working (based on log file). however out of 10 sometimes 8 or sometimes 7 finishes. Rest of them just never come back.
why would that be happening ?
Are they timing out ? Where can I set timeout for my tasks ?
UPDATE
Basically above, I would like each Task to start running as soon as they are called and wait for the response on AWAIT T keyword. I am assuming here that once they finish each of them will come back at Await T and do the next action. I am alraedy seeing this result for 7 out of 10 tasks but 3 of them are not coming back.
Thanks
It is hard to say what the issues is without the rest if the code, but you code can be simplified by making ProcessJob synchronous and then calling Task.Run with it.
public JobDto ProcessJob(JobDto jobTask)
{
JobWorker jobWorker = new JobWorker();
return jobWorker.Execute(jobTask);
}
Start tasks and wait for all tasks to finish. Prefer using Task.Run rather than Task.Factory.StartNew as it provides more favourable defaults for pushing work to the background. See here.
for(int i=0; i < jobList.Count(); i++)
{
tasks[i] = Task.Run(() => ProcessJob(jobList[i]));
}
try
{
await Task.WhenAll(tasks);
}
catch(Exception ex)
{
// handle exception
}
First, let's make a reproducible version of your code. This is NOT the best way to achieve what you are doing, but to show you what is happening in your code!
I'll keep the code almost same as your code, except I'll use simple int rather than your JobDto and on completion of the job Execute() I'll write in a file that we can verify later. Here's the code
public class SomeMainClass
{
public void StartProcessing()
{
var jobList = Enumerable.Range(1, 10).ToArray();
var tasks = new Task[10];
//[1] start 10 jobs, one-by-one
for (int i = 0; i < jobList.Count(); i++)
{
tasks[i] = ProcessJob(jobList[i]);
}
//[4] here we have 10 awaitable Task in tasks
//[5] do all other unrelated operations
Thread.Sleep(1500); //assume it works for 1.5 sec
// Task.WaitAll(tasks); //[6] wait for tasks to complete
// The PROCESS IS COMPLETE here
}
public async Task ProcessJob(int jobTask)
{
try
{
//[2] start job in a ThreadPool, Background thread
var T = Task.Factory.StartNew(() =>
{
JobWorker jobWorker = new JobWorker();
jobWorker.Execute(jobTask);
});
//[3] await here will keep context of calling thread
await T; //... and release the calling thread
}
catch (Exception) { /*handle*/ }
}
}
public class JobWorker
{
static object locker = new object();
const string _file = #"C:\YourDirectory\out.txt";
public void Execute(int jobTask) //on complete, writes in file
{
Thread.Sleep(500); //let's assume does something for 0.5 sec
lock(locker)
{
File.AppendAllText(_file,
Environment.NewLine + "Writing the value-" + jobTask);
}
}
}
After running just the StartProcessing(), this is what I get in the file
Writing the value-4
Writing the value-2
Writing the value-3
Writing the value-1
Writing the value-6
Writing the value-7
Writing the value-8
Writing the value-5
So, 8/10 jobs has completed. Obviously, every time you run this, the number and order might change. But, the point is, all the jobs did not complete!
Now, if I un-comment the step [6] Task.WaitAll(tasks);, this is what I get in my file
Writing the value-2
Writing the value-3
Writing the value-4
Writing the value-1
Writing the value-5
Writing the value-7
Writing the value-8
Writing the value-6
Writing the value-9
Writing the value-10
So, all my jobs completed here!
Why the code is behaving like this, is already explained in the code-comments. The main thing to note is, your tasks run in ThreadPool based Background threads. So, if you do not wait for them, they will be killed when the MAIN process ends and the main thread exits!!
If you still don't want to await the tasks there, you can return the list of tasks from this first method and await the tasks at the very end of the process, something like this
public Task[] StartProcessing()
{
...
for (int i = 0; i < jobList.Count(); i++)
{
tasks[i] = ProcessJob(jobList[i]);
}
...
return tasks;
}
//in the MAIN METHOD of your application/process
var tasks = new SomeMainClass().StartProcessing();
// do all other stuffs here, and just at the end of process
Task.WaitAll(tasks);
Hope this clears all confusion.
It's possible your code is swallowing exceptions. I would add a ContineWith call to the end of the part of the code that starts the new task. Something like this untested code:
var T = Task.Factory.StartNew(() =>
{
JobWorker jobWorker = new JobWorker();
jobWorker.Execute(jobTask);
}).ContinueWith(tsk =>
{
var flattenedException = tsk.Exception.Flatten();
Console.Log("Exception! " + flattenedException);
return true;
});
},TaskContinuationOptions.OnlyOnFaulted); //Only call if task is faulted
Another possibility is that something in one of the tasks is timing out (like you mentioned) or deadlocking. To track down whether a timeout (or maybe deadlock) is the root cause, you could add some timeout logic (as described in this SO answer):
int timeout = 1000; //set to something much greater than the time it should take your task to complete (at least for testing)
var task = TheMethodWhichWrapsYourAsyncLogic(cancellationToken);
if (await Task.WhenAny(task, Task.Delay(timeout, cancellationToken)) == task)
{
// Task completed within timeout.
// Consider that the task may have faulted or been canceled.
// We re-await the task so that any exceptions/cancellation is rethrown.
await task;
}
else
{
// timeout/cancellation logic
}
Check out the documentation on exception handling in the TPL on MSDN.

Passing value parameter to Task in c#

I have an issue with passing a long by value to a Task.
I have a list of ID's where I loop through each one, assign to a local variable then pass as a parameter to a new Task. I do not wait for the task to complete before looping round and processing the next ID. I keep an array of Tasks but this is irrelevant.
loop
long ID = list[index];
task[index] = Task.Factory.StartNew(() => doWork(ID));
end loop
If the list contained for example 100 and 200. I would want the first task called with 100
then the second task called with 200. But it does not, doWork receives 200 for both tasks so there is an issue when the value is copied.
I can demonstrate with some simple console code
class Program
{
static void Main(string[] args)
{
long num = 100;
Task one = Task.Factory.StartNew(() => doWork(num));
num = 200;
Console.ReadKey();
}
public static void doWork(long val)
{
Console.WriteLine("Method called with {0}", val);
}
}
The above code will always display
Method called with 200
I modified the code to wait for the task status to switch from WaitingToRun
static void Main(string[] args)
{
long num = 100;
Task one = Task.Factory.StartNew(() => doWork(num));
while(one.Status == TaskStatus.WaitingToRun)
{}
num = 200;
Console.ReadKey();
}
This improves things but not 100% proof, after a few runs I got Method called with 200
Also tried the following
while (true)
{
if (one.Status == TaskStatus.Running | one.IsCompleted == true)
break;
}
but again got 200 displayed.
Any ideas how you can guarantee the value passed to the task without waiting for the task to complete?
Any ideas how you can guarantee the value passed to the task without waiting for the task to complete?
Sure - just create a separate variable which isn't modified anywhere else. You can use a new scope to make that clear:
long num = 100;
Task one;
{
// Nothing can change copyOfNum!
long copyOfNum = num;
one = Task.Factory.StartNew(() => doWork(copyOfNum));
}
You can't change the C# compiler to capture "the value of the variable when delegate is created" rather than capturing the variable, but you can make sure the variable isn't changed afterwards, which accomplishes the same thing.

AggressiveInlining Affects C# async Methods Behaviour

I have a static field of type ConcurrentQueue:
static readonly ConcurrentQueue<int> q = new ConcurrentQueue<int>();
and an async method:
static async Task<int?> NextNum()
{
int? n = await Task.Run<int?>(() =>
{
int i = 0;
if (q.TryDequeue(out i)) return i;
return null;
});
return n;
}
Then I execute this code:
var nt = NextNum();
q.Enqueue(10);
nt.Wait();
Console.WriteLine("{0}", nt.Result.HasValue ? nt.Result.Value : -1);
And the output is 10.
Now I add MethodImpl attribute to my async method:
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
static async Task<int?> NextNum()
{
int? n = await Task.Run<int?>(() =>
{
int i = 0;
if (q.TryDequeue(out i)) return i;
return null;
});
return n;
}
And when I execute the previously mentioned code I get -1.
Question: Does this mean in an async method the returned Task does not start immediately? And if we add MethodImpl (with AggressiveInlining) attribute it starts immediately?
I want to know if a method decorated with AggressiveInlining has any effect on task scheduler behavior.
Your test is nondeterministic, so the results may be different based on changes in timings / thread switches / load on the machine / number of cores / etc.
E.g., if you change your test to:
var nt = NextNum();
Thread.Sleep(1000);
q.Enqueue(10);
then the output is most likely -1 even without AggressiveInlining.
Question: Does this mean in an async method the returned Task does not start immediately? And if we add MethodImpl (with AggressiveInlining) attribute it starts immediately?
Not at all. The task returned by NextNum always starts immediately. However, the task queued to the thread pool by Task.Run may not. That's where you're seeing the difference in behavior.
In your original test, the task queued by Task.Run happens to take long enough that q.Enqueue gets executed before it does. In your second test, the task queued by Task.Run happens to run before q.Enqueue. Both are nondeterministic, and AggressiveInlining just changes the timings.
Update from comments:
I want to know if a method decorated with AggressiveInlining has any effect on task scheduler behavior.
No, it does not.

Task.Factory.StartNew depend on parent thread?

I'm running this thread inside a method from a WCF service library.
The code below is executed at the end of the method. I do this because i don't want the user to wait for a background process to complete that does not affect the output from the WCF to the client.
The problem that i have now is that if i execute that thread and the client gets the response, the parent thread is killed; killing this thread as well. How do i make it so that the parent thread waits for this thread to finish, while performing the rest of the operations?
class Program
{
static void Main(string[] args)
{
Dictionary<string, string> sampleDict = getPopulatedDictionary();
var result = run(sampleDict);
}
public static int run(Dictionary<string, string> sampleDict_)
{
PerformCalculations(sampleDict_);
if (sampleDict_.Keys.Count > 10)
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
backgroundprocess(sampleDict_);
});
}
//after returning i still want it to run
return sampleDict_.Keys.Count;
}
private static void backgroundprocess(Dictionary<string,string> dict)
{
foreach (var k in dict.Keys)
{
dict[k] = new Random().Next(2666).ToString();
}
}
}
In short, i want this method to kick off that thread and move onto return the value X but still wait for that thread to finish AFTER it returns the value.
Couldn't you do it as a continuation of the parent task. So execute
FameMappingEntry.SaveFameDBMap(toSaveIdentifiers); as a continuation of a successful completion of the parent task. And then you can wait on the continutation.
var childTask = parentTask.ContinueWith((pt) =>
{
FameMappingEntry.SaveFameDBMap(toSaveIdentifiers);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
And then you can decide if you want to wait on the child task or use another continuation.
If you aren't going to do anything except wait for the background thread to complete, then you might as well just not create the new background thread in the first place and execute the code in-line.
Try this:
var task = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
lock (toSaveIdentifiers)
{
FameMappingEntry.SaveFameDBMap(toSaveIdentifiers);
}
);
int x = dosomething();
task.Wait();
return x;
You should also lock objects in the thread that uses them, and not some other random thread.

Categories

Resources