I have the following simplified code:
object myVariable;
var myThread = new Thread(async () =>
{
//Do something....
myVariable = //Some result....
});
myThread.Start();
I start myThread on the first line of my method. My understanding is that this thread will then go off and execute, whilst the rest of the method executes simultaneously. Is this correct?
I then reach a point further down in my code where I will use myVariable. Given this, I need my main thread to pause if myThread has not completed.
My understanding is that I can use myThread.Join() to do this. However, once I call .Join it will pause the main thread until myThread has completed.
My question is, if I do this:
Start myThread
THEN
execute other code that takes x seconds
THEN
Call myThread.Join()
In this case, will the join not happen until it reaches that line of code eg join will not take place until after x seconds - or does join look ahead?
I hope this question is clear?
I don't know if I'm understanding correctly, but I'll try to answer you.
To do what you want, you can use something like:
await someMethod().ContinueWith((task) =>
{
someOtherMethodThatUsesTaskResult(task.Result);
//more code
}, TaskContinuationOptions.OnlyOnRanToCompletion);
Of course you can explore the options you have available for the last parameter.
In your case you can use the keyword await (I think for your situation it's better) :
var myTask = asyncTask(); // Launch the Task
// execute other code that takes x seconds
var myVariable = await myTask; // Get the result of the Task (and wait if it was not finished)
Related
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();
EDIT SOLUTION: It appears the solution is to have code like below where I specify TaskContinuationOption.ExecuteSynchronously
public ActionBlock<DomainBaseFile[]> GetActionBlock()
{
var returnActionBlock = new ActionBlock<DomainBaseFile[]>(
n => Work(n)
);
returnActionBlock.Completion.ContinueWith((s, o) => FinalizeFiles(), new object(), TaskContinuationOptions.ExecuteSynchronously);
return returnActionBlock;
}
I have two tasks, FirstTask and SecondTask that are linked. Both tasks have things they have to do on completion i.e
firstTask.Complete.ContinueWith(t => ...
The problem I'm currently having is that the SecondTask Complete.ContinueWith seems to be running before the FirstTask.Complete.ContinueWith and the code is built assuming that First task is done.
I assume this is because the ContinueWith task runs whenever after the Completion signal is sent and isn't subject to ordering like the Complete propagation is.
Is there anyway for me to force SecondTask to not call it's Complete method till the FirstTask.Complete.ContinueWith is done?
Currently, I have
Task.WhenAll(firstBlock.Completion)
.ContinueWith(_ =>
secondBlock.Complete()
);
However, this Task.WhenAll is going to wait for the Completion and not the Completion.ContinueWith function to finish.
Any help is greatly appreciated.
My C# application stops responding for a long time, as I break the Debug it stops on a function.
foreach (var item in list)
{
xmldiff.Compare(item, secondary, output);
...
}
I guess the running time of this function is long or it hangs. Anyway, I want to wait for a certain time (e.g. 5 seconds) for the execution of this function, and if it exceeds this time, I skip it and go to the next item in the loop. How can I do it? I found some similar question but they are mostly for processes or asynchronous methods.
You can do it the brutal way: spin up a thread to do the work, join it with timeout, then abort it, if the join didn't work.
Example:
var worker = new Thread( () => { xmlDiff.Compare(item, secondary, output); } );
worker.Start();
if (!worker.Join( TimeSpan.FromSeconds( 1 ) ))
worker.Abort();
But be warned - aborting threads is not considered nice and can make your app unstable. If at all possible try to modify Compare to accept a CancellationToken to cancel the comparison.
I would avoid directly using threads and use Microsoft's Reactive Extensions (NuGet "Rx-Main") to abstract away the management of the threads.
I don't know the exact signature of xmldiff.Compare(item, secondary, output) but if I assume it produces an integer then I could do this with Rx:
var query =
from item in list.ToObservable()
from result in
Observable
.Start(() => xmldiff.Compare(item, secondary, output))
.Timeout(TimeSpan.FromSeconds(5.0), Observable.Return(-1))
select new { item, result };
var subscription =
query
.Subscribe(x =>
{
/* do something with `x.item` and/or `x.result` */
});
This automatically iterates through each item and starts a background computation of xmldiff.Compare, but only allows each computation to take as much as 5.0 seconds before returning a default value of -1.
The subscription variable is an IDisposable, so if you want to abort the entire query before it completes just call .Dispose().
I skip it and go to the next item in the loop
By "skip it", do you mean "leave it there" or "cancel it"? The two scenarios are quite different. But for both two I suggest you use Task.
//generate 10 example tasks
var tasks = Enumerable
.Range(0, 10)
.Select(n => new Task(() => DoSomething(n)))
.ToList();
var maxExecutionTime = TimeSpan.FromSeconds(5);
foreach (var task in tasks)
{
if (task.Wait(maxExecutionTime))
{
//the task is finished in time
}
else
{
// the task is over time
// just leave it there
// the loop continues
// if you want to cancel it, see
// http://stackoverflow.com/questions/4783865/how-do-i-abort-cancel-tpl-tasks
}
}
One thing to improve is "do you really need to run your tasks one by one?" If they are independent you can run them in parallel.
There is a method that is called continuously in my program and so I want to thread it such that the GUI doesn't freeze whilst it goes about its business.
Thread t = new Thread(Class2.ArrayWorkings(1, MyGlobals.variable1));
t.start();
int[] localVariable1 = ??// I want to move the value returned from the method into localVariable1.
Currently my errors are:
The best overloaded method match for 'System.Threading.Thread.Thread(System.Threading.ParameterizedThreadStart)' has some invalid arguments
&
Argument 1: cannot convert from 'method group' to 'System.Threading.ParameterizedThreadStart'
Currently doing this without threading like:
int[] localVariabl1 = Class2.ArrayWorkings(1, MyGlobals.variable1);
You can fix the constructor by using a lambda:
Thread t = new Thread(() => Class2.ArrayWorkings(1, MyGlobals.variable1));
but that doesn't let you (as Jon notes) get the result straight away - otherwise you are writing synchronous code again. You can instead look to some kind of callback; presumably you need to get back to the UI thread, so:
Thread t = new Thread(() => {
// this runs on the worker
int[] localVariabl1 = Class2.ArrayWorkings(1, MyGlobals.variable1);
this.Invoke((MethodInvoker)delegate {
// now we're back on the UI thread!
update the UI from localVariabl1
});
});
t.Start()
I would probably suggest using the thread-pool, though:
ThreadPool.QueueUserWorkItem(delegate {
// this runs on the worker
int[] localVariabl1 = Class2.ArrayWorkings(1, MyGlobals.variable1);
this.Invoke((MethodInvoker)delegate {
// now we're back on the UI thread!
update the UI from localVariabl1
});
});
You gotto put a ThreadStart Delegate. If you really dont want UI to get affected, u should use BackgroundWorker Class.
One way of doing it is as following:
ThreadStart starter = delegate { Class2.ArrayWorkings(1, MyGlobals.variable1); };
var thread = new Thread(starter);
thread.Start();
Edit: Just saw that you also want to capture the return value from the thread.
You may have to use ThreadPool.QueueUserWorkItem as mentioned in Marc's answer
Something you might want to look at is .NET Framework 4.5 (still in Release Candidate)
Makes life easier with Asynchronous programming.
await operator in .NET 4.5
Tasks provide an easy interface for performing an asynchronous job that will eventually return a value. A Task<string> for example is a task that (eventually) returns a string where a Task<int[]> will return an array of integers.
int[] localVariable1;
// start the task
Task<int[]> myTask = Task.Factory.StartNew<int[]>(() => Class2.ArrayWorkings(1, MyGlobals.Variable1);
// when it is finished get the result and place in local variable
myTask.OnCompleted(task => localVariable1 = task.Result;);
If you want to update a UI compontent once the asynchronous operation is finished, you will have to use Invoke (for winforms). This allows you to work with objects that live on the UI thread (such as buttons and labels).
myTask.OnCompleted(task => localVariable1.Invoke(new Action(() =>
localVariable1.Value = task.Result; )));
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!