-
March 25th, 2013, 08:30 PM
#1
Do other things whilst in a loop
Hi, newbie here, still very much learning.
I'm writing a piece of software in MFC. In one function, I have a while loop using "KeepLooping" as the condition.
"KeepLooping" is a boolean that is set to false by a toolbar button, key press or menu option. The problem is, when the loop starts running, its obviously so busy in the loop that button presses, toolbar buttons and menu clicks do nothing, so the loop never exits.
What's the best (or should that be simplest?) way around this? I remember in VB you could rather crudely just use DoEvents in a loop to make sure it carried on doing other things in the mean time, but I'm guessing its not that simple in C++.
Thanks guys, Matt.
-
March 26th, 2013, 12:52 AM
#2
Re: Do other things whilst in a loop
![Quote](images/misc/quote_icon.png) Originally Posted by nnXen
What's the best (or should that be simplest?) way around this?
The best way around it is simply not to use "busy waiting".
All modern GUIs (and even OSes) are based on the principle of "don't call us, we call you". That is event handling objects register themselves as subscribers to the events they're interested in and then passively just hang around. Then when an even happens all its subscribers (also called listeners) get called in turn. It's considered good manners to finish as quickly and swiftly as possible not to block fellow listeners.
Read the MFC documentation and study examples to see how the event handling mechanism works.
Last edited by nuzzle; March 26th, 2013 at 04:23 AM.
-
March 26th, 2013, 02:46 AM
#3
Re: Do other things whilst in a loop
![Quote](images/misc/quote_icon.png) Originally Posted by nnXen
I remember in VB you could rather crudely just use DoEvents in a loop to make sure it carried on doing other things in the mean time, but I'm guessing its not that simple in C++.
With MFC it's possible, but it's a fragile solution. The better approach is to run the loop in a thread (caled a worker thread). Then you'll need to synchronize the communication between the GUI thread and the worker thread.
See the FAQ for some references on using threads with MFC: http://forums.codeguru.com/showthrea...02#post1201802
If your compiler supports it, you can also use std::thread (or boost::thread).
Cheers, D Drmmr
Please put [code][/code] tags around your code to preserve indentation and make it more readable.
As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky
-
March 26th, 2013, 04:55 AM
#4
Re: Do other things whilst in a loop
What you need to realize about MFC (and Windows programmign in general) is that:
Whilst you are in a messagehandler function... No other messagehandlers will get called other than messages SENT from that one messagehandle.
Read that again. And once more, make sure you fully understand this, it's quite important to understand what that means.
So for example.
if you have an app, and you have a menu-item for "calculate stuff", and you have an OnCalculateStuff() function. then while that OnCalculateStuff() is happening. your UI will be blocked. It won't handle other messages, it won't even repaint itself, you can't click buttons, move the window, minimize/maximize, ...
So conceptually what you're trying to do simply won't work. because while in your "keeplooping" loop, no other messages will be processed.
There are ways around this.
The generally recommended way is using multiple threads, and have your calculations out of the GUI thread and into one or more worker threads. However, multithreading adds a whole slew of additional problems to your application such as synchronisation and making the GUI and worker thread cooperate properly. It isn't really something for a beginning programmer. Make sure you understand windows programmign properly first !
Another way around it is to process messages in your 'keep looping' loop. This is also called "pumping messages". This works, to a degree, and has some issues of itself. It's sort of a 'poor man's approach' to threading and keeping your app going. it's not without it's own perils. and your while keeplooping loop is effectively suspended for the time it is in the other messagehandlers. You'll probably also need guards against certain UI actions. if someone started the keeplooping loop via a menu. you probably want to prevent them from selecting that same menu item again.
-
March 26th, 2013, 03:18 PM
#5
Re: Do other things whilst in a loop
Thanks Guys! That's been a massive help. I'll probably go with the message pumping method for now but threads are certainly something I'll have to look in to for future reference as it seems that's the real way to go about it
-
March 26th, 2013, 05:22 PM
#6
Re: Do other things whilst in a loop
Well the message pumping method works, but it is buggy with UI updating etc. I think I need to start the loop in a thread. I've had a look through various tutorials on AfxBeginThread, but I can't for the life of me get it to work. Basically at the moment, when a toolbar button is clicked I call the function "Playback", which is basically contains a loop which continues to loop until a boolean is set to false. A simplified version of what I have is below;
Code:
void CDAWView::OnPlaybackPlay()
{
Playing = true;
if(!alreadyInLoop) Playback();
}
void CDAWView::OnPlaybackStop()
{
Playing = false;
}
void CDAWView::Playback(void)
{
alreadyInLoop = true;
while(Playing)
{
//Do Things!
}
alreadyInLoop = false;
}
My question is, how can I modify this to run the playback function as a separate thread?
-
March 26th, 2013, 06:04 PM
#7
Re: Do other things whilst in a loop
What does your message pump look like?
-
March 26th, 2013, 06:52 PM
#8
Re: Do other things whilst in a loop
Code:
if(PeekMessage(&msg,0,0,0,PM_REMOVE)) //Check for user input messages (if user is trying to stop playback)
{
if(msg.message==WM_QUIT) break;
DispatchMessage(&msg);
}
It reads button presses etc, but is quite buggy in that it doesn't 'raise' buttons on hover and obviously doesn't continue running the loop if a button press starts another function or opens a dialog... that's why I was thinking it's probably best to start the loop in a worker thread.
-
March 26th, 2013, 08:36 PM
#9
Re: Do other things whilst in a loop
![Quote](images/misc/quote_icon.png) Originally Posted by nnXen
Code:
if(PeekMessage(&msg,0,0,0,PM_REMOVE)) //Check for user input messages (if user is trying to stop playback)
{
if(msg.message==WM_QUIT) break;
DispatchMessage(&msg);
}
It reads button presses etc, but is quite buggy in that it doesn't 'raise' buttons on hover and obviously doesn't continue running the loop if a button press starts another function or opens a dialog... that's why I was thinking it's probably best to start the loop in a worker thread.
That's not quite right.
Code:
MSG msg;
while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
-
March 26th, 2013, 09:40 PM
#10
Re: Do other things whilst in a loop
Brilliant! That works great! Thank you very much
-
March 26th, 2013, 10:42 PM
#11
Re: Do other things whilst in a loop
Finally, if the user closes the application, how can I terminate the loop before it closes (otherwise it throws errors if the loop is still running.) I understand its the WM_CLOSE message in CMainFrame? How can I change the loop condition in CDAWView from there?
-
March 27th, 2013, 04:10 AM
#12
Re: Do other things whilst in a loop
![Quote](images/misc/quote_icon.png) Originally Posted by nnXen
My question is, how can I modify this to run the playback function as a separate thread?
With C++11 support, you can do something like this (you can use the Boost.Thread library instead if you don't have C++11 support).
Code:
#include <mutex>
#include <thread>
class ThreadWrapper
{
public:
void Start()
{
m_Stop = false;
m_Thread = std::move(std::thread(std::bind(&ThreadWrapper::Loop, this)));
}
void Stop()
{
{
std::lock_guard<std::mutex> lock(m_Mutex);
m_Stop = true;
}
m_Thread.join();
}
private:
bool IsStopped() const
{
std::lock_guard<std::mutex> lock(m_Mutex);
return m_Stop;
}
void Loop()
{
while (!IsStopped())
{
// ...
}
}
std::thread m_Thread;
mutable std::mutex m_Mutex;
bool m_Stop;
};
Cheers, D Drmmr
Please put [code][/code] tags around your code to preserve indentation and make it more readable.
As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky
-
March 27th, 2013, 09:46 AM
#13
Re: Do other things whilst in a loop
![Quote](images/misc/quote_icon.png) Originally Posted by nnXen
Finally, if the user closes the application, how can I terminate the loop before it closes (otherwise it throws errors if the loop is still running.) I understand its the WM_CLOSE message in CMainFrame? How can I change the loop condition in CDAWView from there?
That's one of those "and has some issues of itself" I mentioned...
If your other loop can't handle being "aborted" because in the messagepump you close the app. then the only way out is to either disable close while the outer loop is running. or to delay close (signal to the outer loop that it needs to stop, and then post the close from there or something along those lines).
Neither solution is ideal, but things like that also appear in a threading approach. Closing the main GUI thread, terminates worker threads so unless you guard against that and allow worker threads to properly shut down, you have a similar issue there as well.
If you're programmign in MFC, there is another approach to do stuff "in the background" and that's working with the OnIdle() message handler in the app object. It does mean you need to change your "loop" into separated small chunks of work though, and do a small bit of work each OnIdle call. It's an easy solution for some forms of background processing, but it's not a fit-all-problems thing either.
-
March 27th, 2013, 12:36 PM
#14
Re: Do other things whilst in a loop
The solution I've gone for in the end is to intervene the main frame OnClose, and stop the loop in CView before doing the actual close. Seems to work well.
Definitely going to have to attempt threads in future though... although message pumping is working for this project, threading will no doubt be essential in later projects.
Your help has been awesome guys, the apps coming along great Thanks everyone!
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|