Reputation: 1050
I can't figure this out... I have followed several different instructios I have found everywhere, but I can't find out how to not freeze the UI thread whilst performing a few tasks.
I run this when the window loads. In root of window in MainWindow.xaml I have:
<i:Interaction.Triggers>
<i:EventTrigger EventName="ContentRendered">
<i:InvokeCommandAction Command="{Binding WindowLoaded}" />
</i:EventTrigger>
</i:Interaction.Triggers>
That triggers the following:
private void WindowLoadedEx(object p)
{
DBMan();
LoadValues();
//Boot = false;
//MainMenuVisibility = true;
}
DBMan() is negligable, the problem isn't here - it is in LoadValues(). I have chopped and changed this a lot trying out various different things, nothing working.
private void LoadValues()
{
//ThreadStart AddHeroesRef = new ThreadStart(HeroesDBAddHeroes);
//Thread AddHeroesThread = new Thread(AddHeroesRef);
//AddHeroesThread.Start();
////HeroesDBAddHeroes();
//ThreadStart AddCommentsRef = new ThreadStart(HeroesDBAddComments);
//Thread AddCommentsThread = new Thread(AddCommentsRef);
//AddCommentsThread.Start();
////HeroesDBAddComments();
ThreadStart LoadValuesRef = new ThreadStart(LoadValuesThreaded);
Thread LoadValuesThread = new Thread(LoadValuesRef);
LoadValuesThread.Start();
//HeroesDBAssignComments();
//SetDBIDs();
}
private async void LoadValuesThreaded()
{
ThreadStart AddHeroesRef = new ThreadStart(HeroesDBAddHeroes);
Thread AddHeroesThread = new Thread(AddHeroesRef);
AddHeroesThread.Start();
//HeroesDBAddHeroes();
ThreadStart AddCommentsRef = new ThreadStart(HeroesDBAddComments);
Thread AddCommentsThread = new Thread(AddCommentsRef);
AddCommentsThread.Start();
//HeroesDBAddComments();
while (AddHeroesThread.IsAlive || AddCommentsThread.IsAlive)
{
Thread.Sleep(1);
}
HeroesDBAssignComments();
SetDBIDs();
Boot = false;
MainMenuVisibility = true;
}
(As you can see, ive tried changing different things and cant get anything to work).
Basically, both HeroesDBAddHeroes() and HeroesDBAddComments() need to be run together, they have no conflicts with each other etc. I will in future have other methods to run here also, at the same time in seperate threads.
However, I need to wait for these to all be complete before continuing and allowing HeroesDBAssignComments() to run. If everything above isnt complete already, it wont work. Then, I need to change the booleans Boot and MainMenuVisibility once HeroesDBAssignComments is complete.
Upvotes: 0
Views: 2466
Reputation: 19416
Using the Task-based Asynchronous Pattern (TAP) makes problems like this very simple.
Your top level method WindowLoadedEx
can be declared as async void
, as it is basically a glorified event-handler. Then you may use async
/await
with the captured WPF synchronization context to publish UI changes on the correct thread, i.e:
private async void WindowLoadedEx(object p)
{
DBMan();
await LoadValuesAsync();
Boot = false;
MainMenuVisibility = true;
}
private async Task LoadValuesAsync()
{
var addHeroesTask = HeroesDBAddHeroesAsync();
var addCommentsTask = HeroesDBAddCommentsAsync();
// We cannot assign comments or IDs until these have been added
await Task.WhenAll(addHeroesTask, addCommentsTask).ConfigureAwait(false);
// TODO Should these both also be asynchronous?
HeroesDBAssignComments();
SetDBIDs();
}
Notice I have used HeroesDBAddHeroesAsync
and HeroesDBAddCommentsAsync
as I believe these are truly asynchronous database operations.
Read up about the use of async/await here.
Upvotes: 1
Reputation: 5893
Since you didn't specify a .net version i'll just suggest a 4.0 way of dealing with this:
void BlockingMethod() {
Task.Factory.StartNew(() => {
// blocking work here, db loading for example
})
.ContinueWith(result => {
// update control here with the result of result.Result
},
TaskScheduler.FromCurrentSynchronizationContext()
});
}
You might have to fix up the syntax a little.
What does this do?
Basically when BlockingMethod gets called it will start an asynchronous task. This method will be called by the UI in your case, which is why your UI is freezing.
Because we're asynchronous the code we've passed for execution here will be running still, when the method is already left, since its' work is being dealt with by the framework.
Once the heavy loading is done the code in "ContinueWith" will be executed. We're passing TaskScheduler.FromCurrentSynchronizationContext(), because we need to be sure that this code is only executed in the context the method was originally called from (The ui sync context), otherwise you'll get an exception because wpf does not allow this.
Upvotes: 1
Reputation: 2309
You shouldn't use Thread.Sleep()to wait. Use a WaitHandle instead.
The basic way to use a WaitHandle is to invoke WaitOne() on the place where you would ilke to wait and then perform work on another thread and then invoke Set() on the waithandle.
I usually use EventWaitHandle
Upvotes: 0