Multi thread and async at same time - c#

I have the following code:
myObject object1 = null;
Thread obj1Thread = new Thread(() => { object1 = _myService.GetMethod(variable1, variable2); });
obj1Thread.Start();
obj1Thread.Join();
myObject object2 = null;
Thread obj2Thread = new Thread(() => { object2 = _myService.GetMethod2(variable3, variable4); });
obj2Thread.Start();
obj2Thread.Join();
As far as I understand, this code will create 2 new threads, run the specified methods, pause the main thread until both these threads complete, and then continue execution.
Assuming what I say is correct, all fine so far.
Next I want to try this:
myObject object1 = null;
Thread obj1Thread = new Thread(async () => { object1 = await _myService.GetMethod(variable1, variable2); });
obj1Thread.Start();
obj1Thread.Join();
myObject object2 = null;
Thread obj2Thread = new Thread(async () => { object2 = await _myService.GetMethod2(variable3, variable4); });
obj2Thread.Start();
obj2Thread.Join();
Basically adding async and await to each thread.
The compiler accepts this change and it seems to run locally, but is this code ok, and is it likely to cause me any problems further down the line, for example will the threads get confused, fail to wait, mix up results etc.
I have a reasonably good understanding of async and a basic understanding of multi threading, and I cannot think of any reason why this would not work.
The code runs locally, but my worry is that under heavy load on the server issues may appear that were not present in a local version....

The compiler accepts this change and it seems to run locally, but is this code ok, and is it likely to cause me any problems further down the line, for example will the threads get confused, fail to wait, mix up results etc.
I have a reasonably good understanding of async and a basic understanding of multi threading, and I cannot think of any reason why this would not work.
Yes, this code will cause you problems. The threads are not waiting as you expect. You're passing an async void lambda to the Thread constructor, and that thread will exit as soon as it hits the await in that lambda, before it sets the object1/object2 variable. So it's entirely possible those variables remain null after the Join.
The proper solution, as FCin posted, is to use asynchronous concurrency. (I avoid the term "parallel" here to reduce confusion with the Parallel type and the Task Parallel Library). Asynchronous concurrency uses Task.WhenAll:
// Start both methods concurrently
var task1 = _myService.GetMethod(variable1, variable2);
var task2 = _myService.GetMethod2(variable3, variable4);
// (Asynchronously) wait for them both to complete.
await Task.WhenAll(task1, task2);
// Retrieve results.
myObject object1 = await task1;
myObject object2 = await task2;

Your code can be parallel and asynchronously awaited with a single line:
await Task.WhenAll(_myService.GetMethod(variable1, variable2), _myService.GetMethod2(variable3, variable4)).
That's all you need. No threads. No joining. If these methods are truely i/o, there will be no thread.
As always, must read: https://blog.stephencleary.com/2013/11/there-is-no-thread.html
If your methods return different results and need to be assigned to variables, then you can do this:
Task<int> task1 = _myService.GetMethod(variable1, variable2);
Task<string> task2 = _myService.GetMethod2(variable3, variable4);
// Both tasks started.
int a = await task1; // Wait for first task
string b = await task2; // If this already finished, it will just return the result

Your above code doesn't take advantage of threading, it blocks the first thread until it finishes then starts the second
obj1Thread.Join(); instructs your main thread to wait until obj1Thread exits before continuing. This means it will spin up obj1Thread then wait for it to complete, meaning you will:
create thread 1
Run thread 1
Exit thread 1
create thread 2
Run thread 2
Exit thread 2
You want to do:
myObject object1 = null;
Thread obj1Thread = new Thread(async () => { object1 = await _myService.GetMethod(variable1, variable2); });
obj1Thread.Start();
myObject object2 = null;
Thread obj2Thread = new Thread(async () => { object2 = await _myService.GetMethod2(variable3, variable4); });
obj2Thread.Start();
obj1Thread.Join();
obj2Thread.Join();

Related

Why doesn't the code wait for the loop to complete in "wait"?

Thread ThreadWindow = new Thread(async () =>
{
WindowWPF windowWPF = new WindowWPF();
windowWPF.Show();
await Task.Run(() =>
{
while (true)
{
//code
}
});
//code works when it shouldn't be available
});
For some reason, the compiler suggests changing the last line to me: }); in }){}; what is this for?
The idea is to display the loading window and work with the data in parallel, after which the window should close.
It will not, as soon as await is hit, an incomplete Task will be returned and the job within await will be returned as a future callback if it's a bit long running job.
As you have made while(true) making the piece of code to execute infinitely, assuming that you are trying to do call and forget way, If so then don't use await. Also, Instead of you creating new Thread, try making use of Task as below
var task = new Task(() => MyLongRunningMethod(),
TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task.Start();

use Task.Run() inside of Select LINQ method

Suppose I have the following code (just for learninig purposes):
static async Task Main(string[] args)
{
var results = new ConcurrentDictionary<string, int>();
var tasks = Enumerable.Range(0, 100).Select(async index =>
{
var res = await DoAsyncJob(index);
results.TryAdd(index.ToString(), res);
});
await Task.WhenAll(tasks);
Console.WriteLine($"Items in dictionary {results.Count}");
}
static async Task<int> DoAsyncJob(int i)
{
// simulate some I/O bound operation
await Task.Delay(100);
return i * 10;
}
I want to know what will be the difference if I make it as follows:
var tasks = Enumerable.Range(0, 100)
.Select(index => Task.Run(async () =>
{
var res = await DoAsyncJob(index);
results.TryAdd(index.ToString(), res);
}));
I get the same results in both cases. But does code executes similarly?
Task.Run is there to execute CPU bound, synchronous, operations in a thread pool thread. As it is the operation you're running is already asynchronous, so using Task.Run means you're scheduling work to run in a thread pool thread, and that work is merely starting an asynchronous operation, which then completes almost immediately, and goes off to do whatever asynchronous work it has to do without blocking that thread pool thread. So by using Task.Run you're waiting to schedule work in the thread pool, but then not actually don't any meaningful work. You're better off just starting the asynchronous operation in the current thread.
The only exception would be if DoAsyncJob were implemented improperly and for some reason wasn't actually asynchronous, contrary to its name and signature, and actually did a lot of synchronous work before returning. But if it is doing that, you should just fix that buggy method, rather than using Task.Run to call it.
On a side note, there's no reason to have a ConcurrentDictionary to collect the results here. Task.WhenAll returns a collection of the results of all of the tasks you've executed. Just use that. Now you don't even need a method to wrap your asynchronous method and process the result in any special way, simplifying the code further:
var tasks = Enumerable.Range(0, 100).Select(DoAsyncJob);
var results = await Task.WhenAll(tasks);
Console.WriteLine($"Items in results {results.Count}");
Yes, both cases execute similarly. Actually, they execute in exactly the same way.

Run two task asynchronously

I'm running a synchronous method. Inside it I have to run two big method, so I was thinking to run them asynchronously.. I was thinking something like
public void MyFunc()
{
var doWorkTask_1 = DoWork1();
var doWorkTask_2 = DoWork2();
var result1 = await doWorkTask_1;
var result2 = await doWorkTask_2;
if(result1 == result2)
....
Thread.Sleep(syncInterval);
}
To do this i need that:
DoWork1 and DoWork2 are asynchronous;
MyFunc is asynchrous too;
But no method is asynchronous!!!
SO I tried to do in another way:
public void MyFunc()
{
var doWorkTask_1 = Task.Run(() => DoWork1());
var doWorkTask_2 = Task.Run(() => DoWork2());
var result1 = doWorkTask_1.Result;
var result2 = doWorkTask_2.Result;
if(result1 == result2)
....
Thread.Sleep(syncInterval);
}
So, 1st question:
Do I have written same thing in two different ways?
2nd question. I have to run the MyFunc method every X time, so I call it in this way:
Task.Factory.StartNew(MyFunc);
Can I call it simply
MyFunc();
My question is because inside myFunc I have a Thread.Sleep. Can I let sleep the main thread or is better to let sleep a thread inside the main?
I hope I have been clear.
Thank you.
Have I written same thing in two different ways?
No. Your first method will execute two units of work in parallel, and will asynchronously wait on the first one, then the second one.
Your second method will execute two units of work in parallel, and will synchronously wait on the first one, then on the second one.
Can I let sleep the main thread or is better to let sleep a thread
inside the main?
That depends on what your application is doing. You could turn MyFunc to be async so you can use Task.Delay instead, which internally uses a timer and doesn't block (and you may also pass it a CancellationToken if needed):
public async Task MyFuncAsync()
{
// Do work
await Task.Delay(syncInterval);
}
Side note:
It seems to me like you may be using async over sync, which in general is a questionable approach. I would advise against it.
Instead, like in your first example, explicitly invoke Task.Run on these workers:
public async Task MyFuncAsync()
{
var firstTask = Task.Run(() => DoWork1());
var secondTask = Task.Run(() => DoWork2());
await Task.WhenAll(new[] { firstTask, secondTask });
await Task.Delay(syncInterval);
}
Use Task.WhenAll to create a new task, encapsulating both your worker tasks.
Creates a task that will complete when all of the supplied tasks have
completed.
https://msdn.microsoft.com/en-us/library/hh194874%28v=vs.110%29.aspx
public async void MyFunc()
{
var doWorkTask_1 = DoWork1();
var doWorkTask_2 = DoWork2();
var results = await Task.WhenAll(doWorkTask_1, doWorkTask_2);
}
If you can't do async all the way, and by asynchronous you mean that you want to process DoWork1 and DoWork2 concurrently on different threads then you can use Task.Run to offload the work to a different thread and Task.WaitAll to wait synchronously for both tasks to complete:
public void MyFunc()
{
var task1 = Task.Run(() => DoWork1());
var task2 = Task.Run(() => DoWork2());
Task.WaitAll(task1, task2);
if (task1.Result == task2.Result)
{
// ...
}
Thread.Sleep(syncInterval);
}
Now, since this uses 3 threads (two ThreadPool threads in Task.Run and the calling thread blocked on Task.WaitAll) when we only need 2 we can simplify and optimize the example by executing one of the operations on the calling thread:
public void MyFunc()
{
var task1 = Task.Run(() => DoWork1());
var result2 = DoWork2();
if (task1.Result == result2)
{
// ...
}
Thread.Sleep(syncInterval);
}

Odd Task Parallel Library InvalidOperationException

I have the following code to build an advanced data structure which is pulled from SQL Server, then when the retrevial of that data is complete I update the UI. The code used is
private void BuildSelectedTreeViewSectionAsync(TreeNode selectedNode)
{
// Initialise.
SqlServer instance = null;
SqlServer.Database database = null;
// Build and expand the TreeNode.
Task task = null;
task = Task.Factory.StartNew(() => {
string[] tmpStrArr = selectedNode.Text.Split(' ');
string strDatabaseName = tmpStrArr[0];
instance = SqlServer.Instance(this.conn);
database = instance.GetDatabaseFromName(strDatabaseName);
}).ContinueWith(cont => {
instance.BuildTreeViewForSelectedDatabase(this.customTreeViewSql,
selectedNode, database);
selectedNode.Expand();
task.Dispose();
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion,
this.MainUiScheduler);
}
This works as it should on my main development machine; that is, it completes the build of the database object, then in the continuation update the UI and disposes the task (Task object).
However, I have been doing some testing on another machine and I get an InvalidOperationException, this is due to the task.Dispose() on task which still in the Running state, but the continuation cont should never fire unless the task has ran to completion.
Here's what the code looks like in the debugger when the exception is thrown:
I am aware that it almost always unneccessary to call Dispose on tasks. This question is more about why the continuation is firing at all here?**
The reason for this is simple, you are calling Dispose on the continuation itself and not on the first task
Your code consists of:
Task task = null;
var task = <task 1>.ContinueWith(t => {
/* task 2 */
task.Dispose();
});
In the above code, task is equal to the continuation (ContinueWith doesn't pass back the original Task, it passes the continuation) and that's what's getting captured in the closure you're passing to ContinueWith.
You can test this by comparing the references of the Task parameter passed into the ContinueWith method with task:
Task task = null;
var task = <task 1>.ContinueWith(t => {
/* task 2 */
if (object.ReferenceEquals(t, task))
throw new InvalidOperationException("Trying to dispose of myself!");
task.Dispose();
});
In order to dispose of the first, you need to break it up into two Task variables and capture the first Task, like so:
var task1 = <task 1>;
var task2 = task1.ContinueWith(t => {
// Dispose of task1 when done.
using (task1)
{
// Do task 2.
}
});
However, because the previous Task is passed to you as a parameter in the ContinueWith method, you don't need to capture task in the closure at all, you can simply call Dispose on the Task passed as a parameter to you:
var task = <task 1>.ContinueWith(t => {
// t = task 1
// task = task 2
// Dispose of task 1 when done.
using (t)
{
// Do task 2.
}
});
I'm pretty sure you are trying to do above is equivelent to:
task = Task.Factory.StartNew(() => ...);
task.ContinueWith(cont => { ... task.Dispose(); });
However, what gets assigned to task variable with your code will be the ContinueWith work item, not the origninal StartNew work item.
More importantly, you probably don't even need to worry about task.Dispose() in this scenario.
The only time there is any real value in doing task.Dispose() is when there is a task.Wait() involved somewhere, which allocates an OS wait handle resource under the covers.
More info:
http://social.msdn.microsoft.com/Forums/en/parallelextensions/thread/7b3a42e5-4ebf-405a-8ee6-bcd2f0214f85

Task workflow sequence is wrong

With the code below, the final UI updates made in the final ContinueWith never take place. I think it is because of the Wait() I have at the end.
The reason I am doing that is because without the Wait, the method will return the IDataProvider before its finished being constructed in the background.
Can someone help me get this right?
Cheers,
Berryl
private IDataProvider _buildSQLiteProvider()
{
IDataProvider resultingDataProvider = null;
ISession session = null;
var watch = Stopwatch.StartNew();
var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
// get the data
var buildProvider = Task.Factory
.StartNew(
() =>
{
// code to build it
});
// show some progress if we haven't finished
buildProvider.ContinueWith(
taskResult =>
{
// show we are making progress;
},
CancellationToken.None, TaskContinuationOptions.None, uiContext);
// we have data: reflect completed status in ui
buildProvider.ContinueWith(
dataProvider =>
{
// show we are finished;
},
CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, uiContext);
try {
buildProvider.Wait();
}
catch (AggregateException ae)
{
foreach (var e in ae.InnerExceptions)
Console.WriteLine(e.Message);
}
Console.WriteLine("Exception handled. Let's move on.");
CurrentSessionContext.Bind(session);
return resultingDataProvider;
}
====
just to be clear
I am not having trouble talking to the ui thread. The first continue with updates the ui just fine. The trouble I am having is the timing of the last ui update and the return of the data provider.
I commented out some of the code to reduce the noise level in tis post and focus on the task sequencing.
====
ok, working code
private void _showSQLiteProjecPicker()
{
var watch = Stopwatch.StartNew();
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
ISession session = null;
// get the data
var buildProvider = Task.Factory.StartNew(
() =>
{
var setProgress = Task.Factory.StartNew(
() =>
{
IsBusy = true;
Status = string.Format("Fetching data...");
},
CancellationToken.None, TaskCreationOptions.None, uiScheduler);
var provider = new SQLiteDataProvider();
session = SQLiteDataProvider.Session;
return provider;
});
buildProvider.ContinueWith(
buildTask =>
{
if(buildTask.Exception != null) {
Console.WriteLine(buildTask.Exception);
}
else {
Check.RequireNotNull(buildTask.Result);
Check.RequireNotNull(session);
_updateUiTaskIsComplete(watch);
CurrentSessionContext.Bind(session);
var provider = buildTask.Result;
var dao = provider.GetActivitySubjectDao();
var vm = new ProjectPickerViewModel(dao);
_showPicker(vm);
}
},
CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, uiScheduler);
}
UPDATE BELOW
This code doesn't look like it warrants TPL to me. Looks like maybe a good use for a BackgroundWorker instead!
Either way, the updates are probably not taking place because you can't update the UI from a separate thread -- you need to run the update on the UI thread. You should use the Dispatcher for this (http://stackoverflow.com/questions/303116/system-windows-threading-dispatcher-and-winforms contains info for both WPF and WinForms)
Update:
So I obviously missed some of the code so here's a revised answer. First of all, Nicholas is correct -- .ContinueWith returns a new task (http://msdn.microsoft.com/en-us/library/dd270696.aspx). So instead of
var result = Task.Factory.StartNew(...);
result.ContinueWith(...);
you probably want to create a new task and then make all the ContinueWith() calls and assign to the task and then call .Start() on the task. Something like:
var task = new Task(...).ContinueWith(...);
task.Start();
However, there is a flaw in the design to begin with (as I see it)! You're trying to run this code async, wihch is why you're using threads and TPL. However, you're calling buildProvider.Wait(); on the UI thread which blocks the UI thread until this task completes! Aside from the issue of repainting the UI in the ContinueWith() while the UI thread is blocked, there's no benefit to multithreading here since you're blocking the UI thread (a major no-no). What you probably want to do is stick the Bind()-ing inside a ContinueWith or something so that you don't have to call Wait() and block the UI thread.
My $0.02 is that if you expect the query to take a long time what you really want is 2 threads (or tasks in TPL)-- one to perform the query and one to update the UI at intervals with status. If you don't expect it to take so long I think you just want a single thread (Task) to query and then update the UI when it's done. I would probably do this via BackgroundWorker. TPL was built for managing lots of tasks and continuations and such but seems overkill for this kind of thing -- I think you could do it using a BackgroundWorker in a lot less code. But you mention you want to use TPL which is fine, but you're going to have to rework this a bit so that it actually runs in the background!
PS - you probably meant to put the Console.WriteLine("Exception handled. Let's move on."); inside the catch
I'm a little hazy, but last time I used the TPL I found it confusing. ContinueWith() returns a new Task instance. So you need to assign the second ContinueWith() result to a new variable, say var continuedTask = builderProvider.ContinueWith(...), and then change the last one to reference continuedTask.ContinueWith() instead of buildProvider.ContinueWith(). Then Wait() on the last Task.
Hope that helps!

Categories