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; )));
Related
I wanted to know how to reuse a thread. I have a websocket connection that constantly sends messages which need some computation to be done. I want to add this computation to a thread, but don't want to create a new thread everytime. How can I make it so that the thread is reused?
client.MsgRecieved.Subscribe(info =>
{
Thread t = new Thread(() => Do_work(info));
};
Is there a way I can create a thread, name it and then just add Do_work() on that thread?
Edit:
I get multiple messages from the websocket per second. I rather have them wait in a single queue, rather than all run on a new thread.
The simplest pattern is simply
client.MsgRecieved.Subscribe(async info =>{
await Task.Run(Do_work(info));
});
Which queues the method to run on the built-in threadpool.
If you want to queue the messages to run on a single background thread, you can use a BlockingCollection, something like:
var workQueue = new System.Collections.Concurrent.BlockingCollection<Object>();
var workThread = new System.Threading.Thread(() =>
{
foreach (var work in workQueue.GetConsumingEnumerable())
{
Do_Work(work);
}
});
workThread.Start();
then
client.MsgRecieved.Subscribe(info => {
workQueue.Add(info);
};
I am in a situation where I have to spawn a new thread manually, so I am able to can call .SetApartmentState(ApartmentState.STA). This means (as far as I know) that I cannot use Task. But I would like to know when the thread was done running, something like the await which works with async. However, the best I can come up with is a loop, constantly checking Thread.IsAlive, like this:
var thread = new Thread(() =>
{
// my code here
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
while(thread.IsAlive)
{
// Wait 100 ms
Thread.Sleep(100);
}
This should work (as long as the thread don't end up stalling), but it seems kind of clumsy. Isn't there a more clever way to check when the thread is done (or dead)?
It is only to avoid blocking the GUI thread, so any minor performance hits are fine (like some hundred milliseconds).
Here is an extension method you could use to enable the awaiting of threads (inspired from this article: await anything).
public static TaskAwaiter GetAwaiter(this Thread thread)
{
return Task.Run(async () =>
{
while (thread.IsAlive)
{
await Task.Delay(100).ConfigureAwait(false);
}
}).GetAwaiter();
}
Usage example:
var thread = new Thread(() =>
{
Thread.Sleep(1000); // Simulate some background work
});
thread.IsBackground = true;
thread.Start();
await thread; // Wait asynchronously until the thread is completed
thread.Join(); // If you want to be extra sure that the thread has finished
Could you use the BackgroundWorker class? It has an event that reports when its finished.
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)
I understand that the .Join() causes the threads to pause and wait till a thread finishes its work but how can I avoid the UI from getting frozen? This is what my codes look like"
Thread dataThread = new Thread(()=> data = getData(id));
dataThread.Start();
dataThread.Join();
Thread storingThread = new Thread(()=> storeData(data));
storingThread.Start();
I need to have the Join since the first thread returns an object containing data that needs to be stored through the second thread. But this causes a UI freeze. How can I implement these in maybe a Background thread? What do yall think I should change?
If you are using .Net framework >= 4.5 you can use Tasks
await Task.Run(() => data = getData(id));
await Task.Run(() => storeData(data));
Or in one command
await Task.Run(() => storeData(getData(id)));
If you don't have to wait till it's finished you can also do:
Task.Run(() => storeData(getData(id)));
It seems you don't need two threads:
Thread dataThread = new Thread(() => storeData(getData(id)));
dataThread.Start();
Note, that Task is preferable to Thread. Also, you probably should make use of await.
The answer has already been given. Just as an extra, I give mine.
You can also use ContinueWith like this:
Task<string>.Factory.StartNew(() => "Hey!").ContinueWith(t => Console.WriteLine(t.Result));
Put the whole work into one thread so the UI doesn't stop:
ThreadPool.QueueUserWorkItem( () => storeData(getData(id)));
Or for .Net 4
Task.Factory.StartNew(() => storeData(getData(id)));
Use the async / await keywords. Small example code:
private async void Method()
{
var result = await ExecuteAsync();
// result == true
}
private async Task<bool> ExecuteAsync()
{
//run long running action
return true;
}
In .net 4.0 you need to install Microsoft.Bcl.Async to use this feature.
A good introduction in this feature can be read on http://blog.stephencleary.com/2012/02/async-and-await.html
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!