Running concurrent code on the current thread - c#

I am trying to run concurrent C# code on a single thread.
My goal is to call two or more functions simultaneously. Those functions consist in a long treatment which is not CPU-bound, i.e., fetching data from a database. I would like to start executing those functions at the same moment so that they all reach the point when they call (synchronously) a deeper part of the backend and "wait" for the backend's result (which can take up to several seconds). Those functions are not dependant on one another.
My work environment is very restricted and I cannot write code which could lead to spawning additional threads, and I am provided with synchronous functions which execute a database fetch by waiting until it's finished.
I've tried searching for a similar problem and could only find one post which relates. Most searches revolved around Tasks, custom thread pools and custom job scheduler.
Does anybody have a solution to my initial problem, and/or can anybody confirm whether it's even possible in C# to dispatch slices of execution time between several functions on the same thread the way an operating system round-robin scheduler would?
To illustrate with tasks, the expected behaviour would be to start all three tasks at the same time to reach the Thead.Sleep(1000) instruction as soon as possible, the difference being that the sleeping timer would tick whether the enclosing task is active or not.
public static void Main()
{
var task1 = new Task(() =>
{
Console.WriteLine("Task {0} (syncTask) executing on Thread {1}", Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000); // this would be the backend call, which would resolve in about one second whether this code actively waits for it or not
Console.WriteLine("Task {0} (syncTask) executing on Thread {1}", Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
});
var task2 = new Task(() =>
{
Console.WriteLine("Task {0} (syncTask) executing on Thread {1}", Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000); // this would be the backend call, which would resolve in about one second whether this code actively waits for it or not
Console.WriteLine("Task {0} (syncTask) executing on Thread {1}", Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
});
var task3 = new Task(() =>
{
Console.WriteLine("Task {0} (syncTask) executing on Thread {1}", Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000); // this would be the backend call, which would resolve in about one second whether this code actively waits for it or not
Console.WriteLine("Task {0} (syncTask) executing on Thread {1}", Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
});
// note: comment out either method 1 or method 2
// method 1: is single threaded but synchronous
task1.RunSynchronously();
task2.RunSynchronously();
task3.RunSynchronously();
// method 2: is async but multithreaded
task1.Start();
task2.Start();
task3.Start();
}

Related

Trying to understand async/await in c#

I have written a method using async keyword, but cannot understand the outcome of it, and the reason it doesn't really work:
static async void ExampleWithValueReturned_Async(Stopwatch sw, SlowOperation opr1, SlowOperation opr2)
{
Console.WriteLine("Started processing ASYNC. Start: {0}", sw.Elapsed);
sw.Start();
Console.WriteLine("2 Step0: {0}", sw.Elapsed);
var sum1_2 = await Task<double>.Factory.StartNew(() => opr1.PerformSlowOperation(1, true));
Console.WriteLine("2 Step1: {0}", sw.Elapsed);
var sum2_2 = await Task<double>.Factory.StartNew(() => opr2.PerformSlowOperation(1, true));
Console.WriteLine("2 Step2: {0}", sw.Elapsed);
Console.WriteLine("Operation 1 gave sum: {0}", sum1_2.ToString("0.##"));
Console.WriteLine("Operation 2 gave sum: {0}", sum2_2.ToString("0.##"));
Console.WriteLine("Stopped processing ASYNC. Stop: {0}", sw.Elapsed);
sw.Stop();
sw.Reset();
Console.WriteLine("");
}
I was expecting this to run around twice as fast as inline processing, since PerformSlowOperation would run in parallel. My expectation was that producing sum1_2 and sum2_2 will be happening at the same time, while analysis of the outcome, based on Stopwatch logs, shows that both values are processed in exactly the same way as in the inline processing case. Overall time is also the same in both cases. Why is it so?
The C# await keyword does exactly what the word says, it waits for the function to finish executing before proceeding to the next line. If you didn't use the await keyword then it will run in the background and continue executing the rest of the code, however this doesn't allow you to do anything with the results.
To run both of the tasks in parallel you will need to explicitly state to wait for all of the tasks to finish before continuing rather than just waiting for each one.
Task t1 = Task<double>.Factory.StartNew(() => opr1.PerformSlowOperation(1, true));
Task t2 = Task<double>.Factory.StartNew(() => opr2.PerformSlowOperation(1, true));
await Task.WhenAll(t1,t2);
var sum1_2 = t1.Result;
var sum2_2 = t2.Result;
The above code will wait until both finish, running them in parallel before continuing with the rest of the code.
The reason the keyword exists is not to enable parallel tasks, it is to enable applications to wait for a single asynchronous method to finish before executing the rest of the code. It is very useful in ASP.NET where a long running task will hold up threads wastefully, using await tells ASP.NET that it can give up the control of the thread to another request until the long running request of the previous task finishes then when it does finish give it back control of the thread.

If async/await doesn't create new thread then explain this code

I've read this thread which claims with reference to msdn with idea that async/await doesn't create new threads. Please look at following code:
static class Program
{
static void Main(string[] args)
{
var task = SlowThreadAsync();
for(int i = 0; i < 5; i++)
{
Console.WriteLine(i * i);
}
Console.WriteLine("Slow thread result {0}", task.Result);
Console.WriteLine("Main finished on thread {0}", Thread.CurrentThread.ManagedThreadId);
Console.ReadKey();
}
static async Task<int> SlowThreadAsync()
{
Console.WriteLine("SlowThreadAsync started on thread {0}", Thread.CurrentThread.ManagedThreadId);
await Task.Delay(2000);
Console.WriteLine("SlowThreadAsync completed on thread {0}", Thread.CurrentThread.ManagedThreadId);
return 3443;
}
}
As result of this code I got different ThreadId. Why the same thread gets different ThreadId?
You're using a console application for your example. This effects greatly the outcome of your test. A console application has no custom SynchronizationContext (like Winforms, WPF and ASP.NET have), hence it uses the ThreadPoolTaskScheduler to schedule continuations on an arbitrary thread-pool thread. Try this same example in a UI application and you'll see the continuation invoked on the same thread.
What the articles you linked are trying to get across is that calling async methods does not guarantee that any of the code runs on a separate thread. So if you do want to guarantee this, you have to do it manually. In particular they're not trying to say that an async method will always run on the same thread as the calling method, because this is blatantly false in many scenarios.
I know its been a bit since the question was asked, but I ran into this problem and have come up with more details on why/when this happens. await works by scheduling the code following the await to be run after the awaited task finishes, on whatever thread the threadpool finds convenient. Oftentimes this seems to be on the same thread as the awaited task. Not sure how this plays with SynchronizationContext mentioned in other answers.
I have noticed an exception is when the awaited task finished quickly, it seems like their isn't enough time to place the code on the callback, so the code ends up being called on a third thread.

Calling Task.wait may not wait if the task has not yet started?

I was reading Jeffrey Richter's clr via c# book and felt uncomfortable reading that task wait may not always wait and I quote
"When a thread calls the Wait method, the system checks if the Task
that the thread is waiting for has started executing. If it has, then
the thread calling Wait will block until the Task has completed
running. But if the Task has not started executing yet, then the
system may (depending on the TaskScheduler ) execute the Task by
using the thread that called Wait . If this happens, then the thread
calling Wait does not block; it executes the Task and returns
immediately."
Can some one please share more insight and in which case can such a scenario may happen?
I think this is unfortunately phrased. It's not that the Wait call returns before the task has finished executing; it's that the thread calling Wait may end up executing the task itself, rather than just blocking idly.
Sample code:
using System;
using System.Threading;
using System.Threading.Tasks;
class Test
{
static void Main()
{
// Make sure DemonstrateIssue is already called in a ThreadPool
// thread...
Task task = Task.Run((Action) DemonstrateIssue);
task.Wait();
}
static void DemonstrateIssue()
{
Console.WriteLine("DemonstrateIssue thread: {0}",
Thread.CurrentThread.ManagedThreadId);
Action action = () => Console.WriteLine("Inner task thread: {0}",
Thread.CurrentThread.ManagedThreadId);
Task task = new Task(action);
// Calling Start will just schedule it... we may be able to Wait
// before it actually executed
task.Start();
task.Wait();
}
}
Output every time I've run it:
DemonstrateIssue thread: 3
Inner task thread: 3
This takes advantage of the fact that the thread pool doesn't spin up threads immediately on demand - it waits for a while to see if an existing thread will become available before starting another one. If you add Thread.Sleep(5000); before the call to task.Wait(), you'll see the two tasks end up on different threads.

Task is being scheduled on the same thread as the caller's

My application has a View Model which contains a Lazy<BitmapImage> field. The field is populated using a service call to the server. In cases where the image is large, it takes a few seconds for the server to return the image (which is in fact a byte[]) therefore the UI is blocked. To prevent this, I put the service call in a Task, so that a background thread gets the image and then calls the OnPropertyChanged to let the UI know the image is returned:
Console.WriteLine("Outside Task ThreadID: {0}",
Thread.CurrentThread.ManagedThreadId);
Task.Factory.StartNew(() =>
{
Console.WriteLine("Inside Task ThreadID: {0}", Thread.CurrentThread.ManagedThreadId);
return Utilities.ConvertByteToImage(
SessionService.GetUserInformation(UserInfo.From).ProfilePicture);
}).ContinueWith(resultToken =>
{
m_lazyProfilePicture = new Lazy<BitmapImage>(() =>
{
return (resultToken.Result == null) ? Utilities.DefaultProfilePicture.Value : resultToken.Result;
});
OnPropertyChanged("ProfilePicture");
});
I noticed that even after putting the service call in a Task, the UI is till blocked. So added those Console.WriteLine lines to see the thread IDs. Surprisingly enough, both of them report the same thread ID (this seems to happen only in this case.I tried it with other tasks in the project, and they all report different IDs). Any idea what's going on here? Does it have anything to do with the BitmapImage? For some reason the scheduler decides to put the task in the same thread, but I don't understand why. Any suggestions are welcome!
StartNew doesn't ensure that the task is run in a new thread. It uses TaskScheduler.Current to schedule the new task. In many places throughout your code this will be null. When it is null, then TaskScheduler.Default will be used, which will schedule the delegate to run in the thread pool.
In your particular case Current is not null. It is the representation of some task scheduler that schedules the delegates to run in the UI thread.
One way this may have happened is if the code that you are running is the result of a call to StartNew or ContinueWith with the UI synchronization context. During the delegates executed in either case it will set the current scheduler to be one that is based on the SynchronizationContext provided, namely the UI context.
If you use Task.Run you avoid the issue; it will always use the default task scheduler instead of the current one.
Your other option is to explicitly state you want the default task scheduler:
Task.Factory.StartNew(() => { }
, CancellationToken.None
, TaskCreationOptions.None
, TaskScheduler.Default);

Task stays in WaitingToRun state for abnormally long time

I've got a program that handles a variety of tasks running in parallel. A single task acts as a manager of sorts, making sure certain conditions are met before the next task is ran. However, I've found that sometimes a task will sit in the WaitingToRun state for a very long time. Here's the following code:
mIsDisposed = false;
mTasks = new BlockingCollection<TaskWrapper>(new ConcurrentQueue<TaskWrapper>());
Task.Factory.StartNew(() => {
while (!mIsDisposed) {
var tTask = mTasks.Take();
tTask.task.Start();
while (tTask.task.Status == TaskStatus.WaitingToRun) {
Console.WriteLine("Waiting to run... {0}", tTask.task.Id);
Thread.Sleep(200);
}
tTask.ready.Wait();
}
mTasks.Dispose();
});
DoWork();
DoWork();
DoWork();
DoWork();
DoWorkAsync();
DoWorkAsync();
DoWorkAsync();
DoWorkAsync();
DoWorkAsync();
DoWork();
TaskWrapper is very simply defined as:
private class TaskWrapper
{
public Task task { get; set; }
public Task ready { get; set; }
}
And tasks are only currently added in 2 places:
public void DoWork()
{
DoWorkAsync().Wait();
}
public Task DoWorkAsync()
{
ManualResetEvent next = new ManualResetEvent(false);
Task task = new Task(() => ActualWork(next));
Task ready = Task.Factory.StartNew(() => next.Wait());
mTasks.Add(new TaskWrapper() {
task = task,
ready = ready
});
return task;
}
Where ActualWork(next) calls next.Set().
This queues work and waits until next has been set before allowing the next work item to proceed. You can either wait for the entire task to finish before continuing by calling DoWork() or queue multiple tasks at once (which are supposed to run after next has been set).
However, when adding a task via DoWorkAsync(), after calling tTask.task.Start(), tTask.task sits in the WaitingToRun state for a loooong time (like 30 seconds to a minute), then magically starts running. I've monitored this using the while loop, and Waiting To Run... # will display for quite some time.
Calling DoWork() always runs immediately. I'm sure this has something to do with calling Wait on the task that is set to run.
I'm at a loss, here.
UPDATE:
I've managed to make the code work, but I'd still like to know why there's an issue in the first place.
After some experimental changes, I've managed to fix my own problem, but it's more of a "Oh, so I just can't do that" rather than a good fix. It turns out my problem was enqueuing tasks to run too quickly. By modifying DoWorkAsync() to no longer use Task.Factory.StartNew and changing tTask.ready.Wait() to tTask.ready.RunSynchronously I've managed to solve my issue.
Is there a reason the TaskScheduler is delaying the scheduling of my tasks? Am I saturating some underlying resources? What's going on here?
The threads will be run in the system's thread pool. The thread pool has a minimum number of threads available at all times (see ThreadPool.SetMinThreads()). If you try to create more than that many threads, a delay of approximately 500ms will be introduced between each new thread starting.
There is also a maximum number of threads in the thread pools (see ThreadPool.GetMaxThreads()), and if you reach that limit no new threads will be created; it will wait until an old thread dies before scheduling a new one (or rather, rescheduling the old one to run your new thread, of course).
You are unlikely to be hitting that limit though - it's probably over 1000.
Ok, I've just been faced with a similar issue. A bit of code that created and started a task ran, but the task never started (it just changed status to WaitingToRun)
Having tried the other options in this thread to no avail I thought about it a bit more, and realised that the code that was calling this method was itself called in a continuation task, that had been specified to run on the UI task scheduler (As it needed to update the UI)...
So something like
void Main()
{
var t1 = new Task(() => Console.WriteLine("hello, I'm task t1"));
t1.ContinueWith(t => CreateAndRunASubTask(), TaskScheduler.FromCurrentSynchronizationContext());
t1.Start();
Console.WriteLine("All tasks done with");
}
// Define other methods and classes here
public void CreateAndRunASubTask()
{
var tsk = new Task(() => Console.WriteLine("hello, I'm the sub-task"));
tsk.Start();
Console.WriteLine("sub-task has been told to start");
tsk.Wait();
// the code blocks on tsk.Wait() indefinately, the tsk status being "WaitingToRun"
Console.WriteLine("sub-task has finished");
}
The fix turned out to be pretty simple - when specifying the continuation task you need to specify the TaskContinuationOption: TaskContinuationOptions.HideScheduler
This has the effect of... (taken from the XML comment)
Specifies that tasks created by the continuation by calling methods
such as System.Threading.Tasks.Task.Run(System.Action) or
System.Threading.Tasks.Task.ContinueWith(System.Action{System.Threading.Tasks.Task})
see the default scheduler (System.Threading.Tasks.TaskScheduler.Default) rather
than the scheduler on which this continuation is running as the current scheduler.
ie (in my example)
t1.ContinueWith(t =>
CreateAndRunASubTask(),
System.Threading.CancellationToken.None,
TaskContinuationOptions.HideScheduler,
TaskScheduler.FromCurrentSynchronizationContext());
Hope this helps someone, as it stumped me for a good while!
Just faced similar issue.
I have a bunch of similar tasks running inifite loops, one of that tasks from time to time stays in WaitingToRun state permamently.
Creating tasks in that way did the trick for me:
_task = new Task(() => DoSmth(_cancellationTokenSource.Token), TaskCreationOptions.LongRunning);
_task.Start();

Categories

Resources