问题
I would like to add a button to my form with which a running process (loop which runs several thousand times) can be terminated if the user wishes to stop the process for any reason or load another file.
The program has a part which performs an interpolation for multiple cases and creates and saves a text file for each case. Once the process is launched (this is done by pressing a button after the necessary files have been loaded), it starts running and cannot be stopped until it is completed. The duration of this process could be anything between minutes and hours depending on the size of certain files.
I have copied a code snippet from the part which is called when the button is pressed (changed names and certain parts and ommitted what follows the first if block as it is irrelevant here).
void __fastcall TMapperForm::bt_InterpolationClick(TObject *Sender)
{
int i;
if(someflagused == true)
{
different_cases_total = 0;
for(i=0; i<something_counter; i++)
{
CleanStuff();
FunctionReadingFiles(list_of_stuff->Items->Strings[i]);
InterpolatingFunction();
different_cases_total+= no_cases;
}
}
}
As written above, I would like to create another button which can kill/terminate the process. My main problem is that when the program runs, it freezes and I see no way to interrupt the loop.
Is there any way to add a button which remains active even when the loop is running and can terminate the process when clicked?
回答1:
As I see it you are interpolating inside VCL which means that VCL is frozen
The VCL/WinProc function is stopped until your computation is done so no components will work (no buttons,timers,sliders,...)
How to repair it?
move the computation to thread
Threads are great for this but you need to do some synchronization and
Avoid access of VCL components and visual stuff WinAPI calls from the thread !!!
So if you need to draw something pass it to your window and then draw from App main thread (inside timer or something).
Inside the thread scan some
volatile bool stop;
and iftrue
break from for loop. Then on start of thread setstop=false;
and on your button click eventstop=true;
move your computation to
OnIdle
event of applicationjust add
void __fastcall MyOnIdle(TObject* Sender, bool &Done);
inside your form class header file(*.h)
and then to your form(*.cpp)
add:void __fastcall TForm1::MyOnIdle(TObject* Sender, bool &Done) { for (int i=0;i<1000;i++) // iterate so many times it does not slow the App too much { if (computation_done) { Done=true; Application->OnIdle=NULL; return; } // here iterate your computation } Done=false; // if you let Done be false all the time the CPU would be 100% loaded !!!) }
now when you want to start the computation you just do
Application->OnIdle=MyOnIdle;
and for stopping it doApplication->OnIdle=NULL;
changeTForm1
to class name of your form.OnIdle event is part of VCL main thread so you can access any VCL stuff at will so no problems with access violations and invalidating WinAPI like there are with threads also you do not need to use the
volatile
anymoreCan use VCL timers
it is almost the same as
OnIdle
event just instead of stopping after1000
(or any other number) loops stop aftertime
is passed. For that you need to scan time for example withLARGE_INTEGER i; QueryPerformanceFrequency(&i); double freq=double(i.QuadPart); QueryPerformanceCounter(&i); double cnt=double(i.QuadPart);
freq
is in [Hz] andcnt
is actual count so take onecnt0
value at start of timer inside loop take another onecnt1
andif ((cnt1-cnt0)/freq>double(Timer1->Interval)*0.001*0.9) break;
this way you will run almost the time your timer is firing so the app should not slow down too much on any CPU. If you are running OS without these counters (Win9x ...) you can still use
RDTSC
or some OS time api with high enough resolution
[Notes]
Only the first option is capable of using your interpolation as is for the bullets #2,#3 you need to rewrite it so it can be computed in iterative process until it is done.
回答2:
The is no problem to give TButton. Problem is to be prepared to events. Such loop catch activity and Your app don't respond to events.
bool BreaKtheLoop = false;
void __fastcall TMapperForm::bt_InterpolationClick(TObject *Sender)
{
int i;
if(someflagused) // redundant comparison == true)
{
..
for(very long loop)
{
...
Application->ProcessMessages();
if(BreaktheLoop )
break;
}
}
}
an in new button Click set variable to true;
来源:https://stackoverflow.com/questions/32737268/creating-a-button-with-which-a-running-process-can-be-killed-or-terminated