Click here to Skip to main content
15,885,216 members
Please Sign up or sign in to vote.
3.50/5 (2 votes)
See more:
Hi,

I have a scenario where I am starting a child thread which sets the 'busyIndicator.IsBusy' as true before doing some time taking stuff in main UI thread.
and when the execution in main UI thread is done I am again setting IsBusy as false.
But the problem is that the code in the delegate of the child thread doesn't get executed until the main thread is done doing its stuff making the use of the BusyIndicator pointless.
Is there any way to make the main thread wait until the child thread is done doing its task.
Here is my code sample:
C#
Thread thread = new System.Threading.Thread(delegate()
            {
                
                testBusyIndicator.Dispatcher.Invoke(DispatcherPriority.Send,
                   (ThreadStart) delegate
                    {
                        testBusyIndicator.IsBusy = true;
                    });
                
            });
            thread.Start();
            
            //DoTimeTakingStuff()
            testBusyIndicator.IsBusy = false; // It can also be done in a separate thread but not needed I think.
            thread.Abort();


Thanks in advance :)
Posted
Updated 4-Jul-22 23:01pm
v2
Comments
Sergey Alexandrovich Kryukov 21-Sep-11 10:22am    
Why on Earth doing so?! What's the idea?
--SA

First, you should never make main thread (which is your UI thread I assume) to wait for anything. Not only this is bad, this is also never needed.

You code sample makes no sense at all, because the method of your thread really does nothing. Think of what are you doing. You're trying to create a race condition between you UI thread and thread competing over the value of testBusyIndicator; the assignment will be done in some random order. I prefer more descriptive term about race condition — "incorrect dependency on the time of execution". See http://en.wikipedia.org/wiki/Race_condition[^].

Now, I'm not answering what to do, because you don't explaining what you want to achieve. The mistake of you question is trying to ask about something which does not have to have any purpose (and apparently does not have in this case); you should ask a question based on your ultimate purpose, not on your opinion on how it should be achieved, especially when you don't explain what you want to achieve. I'm also not asking about what are you trying to do because it is apparent to me that you don't understand it yourself.

Focus on your ultimate goal. Picture what useful work should your thread do, not changing the status of any indicator. Perhaps you should simply prevent UI from some action until your temporary thread's work is complete. But this is very simple task: you should invoke change of UI status in the very beginning and the very end of the thread's body, that is in the same thread, not too different threads. (By the way the call to Invoke makes sure that actual calls on UI controls are always done in the UI thread anyway.) Be aware of race condition and avoid it.

You will find detailed explanation of how invocation works and code samples in my past answers:
Control.Invoke() vs. Control.BeginInvoke()[^],
Problem with Treeview Scanner And MD5[^].

See also more references on threading:
How to get a keydown event to operate on a different thread in vb.net[^],
Control events not firing after enable disable + multithreading[^].

—SA
 
Share this answer
 
Yes, this is a very common scenario that the designers of WPF did a bad job providing for.

e.g.
C#
StatusbarText = "Saving ...";
await SaveOperation();
StatusbarText = "Save complete";

In the above code the user won't see "Saving ..." appear on screen.

C#
StatusbarText = "Saving ...";
// wait for rendering thread to update text on screen
await SaveOperation();
StatusbarText = "Save complete";

Unfortunately WPF does not provide access to the rendering thread
(or they've hidden it away somewhere)

So you're stuck with having to wait on the main UI thread instead.

I've seen various post with Dispatcher.Invoke or Dispatcher.BeginInvoke but they didn't work for
me, or worked intermittently.

Others suggest re-writing everything to use background threads
(for something that should not need a background thread)

What worked for me was using a DispatcherTimer and Dispatcher.Yield()
If you put a breakpoint on the await SaveOperation() line you can see that the text has been updated

C#
StatusbarText = "Saving ...";
await UIFinishRender();
await SaveOperation();
StatusbarText = "Save complete";


public async Task UIFinishRender()
{
    // want to wait for render thread finish drawing
    RenderCount = 0;    // private int

    DispatcherTimer timer = new DispatcherTimer();
    timer.Interval = TimeSpan.FromMilliseconds(1);
    timer.Tick += timer_Tick;
    timer.Start();

    while (RenderCount < 30)
    {
        await Dispatcher.Yield();
    }
}

void timer_Tick(object sender, EventArgs e)
{
    RenderCount++;
}
 
Share this answer
 
Comments
Graeme_Grant 5-Jul-22 5:22am    
Why are you answering an 11 year old question that was already replied to. There are plenty of current questions that require help.

FWIW, your solution uses TPL. TPL / "the async and await model for asynchronous programming" wasn't released until a whole year after this question was asked in version 5.0 of C#. Read more here: The history of C# - C# Guide | Microsoft Docs[^]

Lastly, for your solution, it's overkill. You can simply use
await SaveOperation().ConfigureAwait(false);

You can read more about it here: ConfigureAwait FAQ - .NET Blog[^]

If the UI thread is still being blocked by your async method, then in you async method you can use Task.Delay(1).ConfigureAwait(false) to release dispatcher for the UI thread, This should not be required if you have coded correctly.
[no name] 6-Jul-22 8:27am    
Was answering it because I experienced the same problem and a search brought me here. The first solution wasn't a solution. Hopefully my answer helps someone else who arrives here when searching. That blog about ConfigureAwait says nothing about the WPF render thread.
Graeme_Grant 6-Jul-22 15:51pm    
No it does not, but it does discuss what I was referring to, the SynchronizationContext used in asynchronous/TPL, not just WPF, and ConfigureAwait in great detail. Stephen Toub is the guru at Microsoft. Google his blog to learn more about what you are using to understand it better.
[no name] 8-Jul-22 10:12am    
I tried the using '.ConfigureAwait(false)' and it did not work. I also tried await Task.Delay(30).ConfigureAwait(false); and it did not work. So I'm sticking with my 'overkill' solution for now. In future I'll try to read Stephen's blog to see if he's got any explanation about how to get at the render thread.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900