Creating a button with which a running process can be killed or terminated

∥☆過路亽.° 提交于 2019-12-11 07:58:52

问题


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?

  1. 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 if true break from for loop. Then on start of thread set stop=false; and on your button click event stop=true;

  2. move your computation to OnIdle event of application

    just 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 do Application->OnIdle=NULL; change TForm1 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 anymore

  3. Can use VCL timers

    it is almost the same as OnIdle event just instead of stopping after 1000 (or any other number) loops stop after time is passed. For that you need to scan time for example with

        LARGE_INTEGER i;
        QueryPerformanceFrequency(&i); double freq=double(i.QuadPart);
        QueryPerformanceCounter(&i); double cnt=double(i.QuadPart);
    

    freq is in [Hz] and cnt is actual count so take one cnt0 value at start of timer inside loop take another one cnt1 and

    if ((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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!