Re: Use a DC in a worker thread
On Sep 26, 9:21 am, Joseph M. Newcomer <newco...@flounder.com> wrote:
See below...
On Thu, 25 Sep 2008 20:37:07 -0700 (PDT), Faisal <faisal...@gmail.com> wr=
ote:
On Sep 25, 3:54 pm, Joseph M. Newcomer <newco...@flounder.com> wrote:
See below....
On Wed, 24 Sep 2008 22:19:41 -0700 (PDT), Faisal <faisal...@gmail.com>=
wrote:
On Sep 24, 8:35 pm, Joseph M. Newcomer <newco...@flounder.com> wrot=
e:
Yes. There is actually an exmaple in the MSDN that does this.
The key here is to make sure that at no time do two threads have DC=
s to the same window,
and that at no time do two threads try to manipulate the same DC.
You cannot do a progress bar only because StretchBlt does not give =
you progress
indications. BUT, if you broke the problem into a sequence of St=
retchBlt calls (say the
image is 1024x1024; if you did StretchBlt of 128x128 pieces, you wo=
uld have 64
opportunities to update the progress bar, one for each little piece=
you did. I don't have
any idea how good the output would look because I don't know how se=
nsitive StretchBlt is
to boundary conditions, but on the whole, you can assume that you a=
re going to get crap.
Serious graphics requires doing decent image convolution algorithms=
that will average
multiple pixels (there's a huge body of literature on this).
However, another solution is to simply StretchBlt into a memory DC =
and only handle the
resulting BLT to the client area DC in the main thread.
Does bliting to a memory DC and then to the device dc have any
performance advantage?
****
Sounds like you're asking "Is doing something twice faster than doing =
something once?" The
answer is obvious.
Generally, this technique is used not for performance reasons but to r=
educe screen
flicker, which can be an important consideration.
****>In my case, I think the application takes the same time.
****
And how did you determine this? If the answer does not include the =
use of
QueryPerformanceCounter then you don't actually know.
joe
****
An advantage of the piecewise transformation (if it produces an acc=
eptable image) is that
you can actually display pieces at a time so the user doesn't just =
see a totally blank
screen. Problem is that you can't have a bitmap selected into tw=
o different DCs at the
same time, so you would have to be careful that you didn't need to =
do this to make it
work, whether the rendering was done to a memory DC or directly to =
the screen.
joe
On Wed, 24 Sep 2008 07:06:26 -0700 (PDT), Faisal <faisal...@gmail.c=
om> wrote:
My application uses StretchBlt() to print some images and graphs.
Since StretchBlt () takes a few minutes to complete, user feels th=
e
application is hanged.
As a solution I would like to move the StretchBlt () to a separate
thread. Is this a good solution? Can I manipulate a DC from a thre=
ad
other than its owner?
Also, I would like to show a progress bar while printing. How woul=
d I
know the steps once the StretchBlt()went to hang?
Joseph M. Newcomer [MVP]
email: newco...@flounder.com
Web:http://www.flounder.com
MVP Tips:http://www.flounder.com/mvp_tips.htm
Joseph M. Newcomer [MVP]
email: newco...@flounder.com
Web:http://www.flounder.com
MVP Tips:http://www.flounder.com/mvp_tips.htm
I didn't measure by time checking. I'm just giving my observation.
I thought this would increase the performance. So I asked that
question.
****
Any time you think you know one implementation is faster than another, bu=
t you have no
basis for this, you have to measure.
****
Now I modified the code to show a progress bar. For this I created a
UI thread before my long operation. In this thread I'm showing a
dialog with progress bar. And in between each small StrechBlt()
operation I tried to update the progress bar.
****
DO NOT create a UI thread that shows a window. Especially NEVER create=
a UI thread that
creates a dialog. This is just out-and-out a mistake. You are going=
to end up with
cross-thread SendMessage operations and you will eventually get screwed b=
y this. The rule
is, the one-and-only GUI thread has responsibility for ALL windows, perio=
d, and NO OTHER
THREAD EVER has a user-visible window.
Creating a UI thread with a progress dialog is the wrong solution. The=
only question is
how long it will take you to discover that the solution doesn't work. =
Post-deployment is
the usual time the potential disasters become real disasters, which is wh=
y this is never
done
(A good predicate to test this: only beginners who don't understand threa=
ding would adopt
this solution; those of us who understand threading would not even consid=
er the
possibility of creating a secondary thread with a dialog. There's seve=
ral reasons we
don't do this, but the fact that we don't should be an important message =
to you)
****
But, to my surprise dialog is not showing up, when i started the new
UI thread. Only when I'm returning from the current function in the
main thread, the dialog is showing. Is this an expected behavior?
While debugging it shows that the thread is starting up, but in the
dialog.create() call hangs and it returns only when the current
command procedure in main thread is returned. The exact function with
hangs is ::CreateDialogIndirect().
****
This would not surprise me in the slightest. Do you understand how int=
erthread messaging
works? Note that because the dialog can't come up until the SendMessag=
e to its parent has
returned, and that can't return if the main thread is busy doing somethin=
g, so you got the
case where the wrong solution fails immediately.
****
A stripped down version of my code is given below.
void CMainFrame::OnUIthrd()
{
CPBThread* pThread = new CPBThread();
pThread->CreateThread(CREATE_SUSPENDED)
pThread->ResumeThread();
****
WHy are you creating it suspended and resuming it immediately? The onl=
y reason to create
it suspended and resume it is so that between these two statements you ca=
n do important
things.
****
for( int i = 0; i < 100; i++ )
{
Sleep( 100 ); // In real code I'm doing the s=
tretch blit from here
pThread->m_nStep = i;
}
****
Absolutely, positively, without any doubt whatsoever, this is going to fa=
il completely.
You are doing your computations in the main GUI thread, which blocks the =
main GUI thread's
message pump, which means the dialog won't come up properly. What part=
of "do the work in
a secondary thread", that we have been saying all along, did you miss? =
We don't say that
because it amuses us to do so; we say it because it is the only sane solu=
tion.
****
SetEvent( pThread->m_hEventKill );
****
What is the purpose of this pointless SetEvent? It cannot possibly be =
associated with
killing the dialog, because the dialog cannot possibly be waiting on an e=
vent. So it is
immediately obvious just by seeing this line that your fundamental design=
is flawed beyond
redemption.
****>}
Thread code is given here
BOOL CPBThread::InitInstance()
{
CProgessDlg* pProDlg = new CProgessDlg();
pProDlg->Create(IDD_PROGRSS, 0 );
****
You are creating a modeless dialog, so the correct code would be
pProDlg->Create(CProgressDlg::IDD);
there is no reason to use the explicit dialog ID
****> pProDlg->ShowWindow(SW_SHOW);
// loop but check for kill notification
while (WaitForSingleObject(m_hEventKill, 0) == WAIT_TIMEOUT)
{
pProDlg->m_Progress.SetPos( m_nStep );
}
****
There is nothing construably sensible here. This merely steps the prog=
ress bar
infinitely, because a 0 delay times out instantly. This thread pins yo=
ur CPU utilization
at 100% because it never stops. Another way to tell a fundamentally fl=
awed design is it
has a line that says
while(WaitForSingleObject( ...) == WAIT_TIMEOUT)
since WaitForSingleObject can return several possible values, and testing=
for just one of
them is not particularly useful.
****
pProDlg->DestroyWindow();
delete pProDlg;
****
ABSOLUTELY WRONG! YOU CANNOT DO THE delete HERE. You MUST do it in=
the PostNcDestroy
handler!
****> pProDlg = NULL;
****
Why do you care, at this point? You should not ever be able to touch t=
his variable after
the return
****
// avoid entering standard message loop by returning FALSE
return FALSE;
}
I think there's some serious mistake in my understanding of UI thread.
How can I solve this problem?
****
Yes, there is. You must not use UI threads with user-visible windows (=
the exceptions to
this are very rare, very exotic, and probably are implemented incorrectly=
almost all the
time). You must not do long computations in the main GUI thread. Yo=
u must not poll
synchronization objects; if you can't use INFINITE, the design is very su=
spect. The cases
of using timeout are now to the level where I don't ever use anything oth=
er than INFINITE
unless I have a compelling reason to do so. You are using the polling =
of an event to
substitute for a positive messaging system (which I showed how to write).=
You have to
eliminate the UI thread as a way to bring up the dialog, bring up the dia=
log in the main
UI thread, use interthread PostMessage to deal with notifications, and do=
all the
computation in a secondary thread. Whether you update the display by u=
sing a DC in the
primary or secondary thread is debatable, but I'd put all the updates in =
the main GUI
thread and let the secondary thread simply tell me when it has a computat=
ion I should care
about.
You can solve the problem by eliminating most of the code you have writte=
n and replacing
with code that more closely resembles what I originally suggested, which =
removes all these
problems (I typed that code in about as fast as I could type, because thi=
s is the
architecture I use for most of my multithreaded code, and I've been ...
read more =BB
Thanks Joe for correcting me.
The reason for going with a UI thread solution is, the Architect of my
project asked me to develop a framework, in which
1. User can give any dialog pointer and ID to this framework, and it
will create the dialog and show it in a new thread.
2. With some mechanism this dialog can be killed from main thread.
With this implementation she can reuse the implementation in several
places.
From your comments I understand that showing a dialog from a UI thread
is a really bad design. And the problem comes when the UI thread try
to send some message to the parent window. In my sample, I specified
the parent as NULL, so the reason for hang may be some other
sendmessage. Is my understanding is correct? So why really the UI
thread need to send messages to Main thread, even if the parent is
NULL?
Also in which cases we need to create UI threads?