Discussions related to Visual Prolog
Peter Muraya
VIP Member
Posts: 147
Joined: 5 Dec 2012 7:29

How to use multi-threading techniques to stop a process

Unread post by Peter Muraya »

Hi,
I have a process that could go on for a long time and sometimes I need to stop it in a much more elegant way than the Ctl-Alt-Del method that I have been using. I know it has to do with running multiple threads using guards, monitors, etc but I'm not sure how to get started. Here is the single thread version of what I am trying to do

Code: Select all

class facts      terminate:() determ. predicates     onPerform : window::menuItemListener. clauses     onPerform(Source, _MenuTag):-          /*          Clear the terminate flag*/          retractall (terminate()),          /*          Display the stop dialog modelessly*/          stop::display(Source)=_,          /*          Go into an endless loop and abort the process when the Stop button on the stop dialog box is clicked*/          foreach std::repeat(), not (terminate()) do                stdio::write("performing\n")          end foreach.      set_terminate():-assertz(terminate()).
The Stop dialog box implements the following responder

Code: Select all

predicates     onStopClick : button::clickResponder. clauses     onStopClick(_Source) = button::defaultAction:-         taskwindow::set_terminate().
How would you turn this single thread example into multi-thread so that Stop works as intended?
Mutall Data Management Technical Support
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post by Thomas Linder Puls »

Your usage of repeat and not(terminate()) like that will never terminate. When terminate succeeds not(terminate) will fail, and then you backtrack into repeat again, so you will be looping between repeat and terminate instead of actually terminating.


You can start the background job in a separate thread, but you should notice that a only the GUI (main) thread owns all the windows and is the only thread that is allowed to update them.

So your background thread can for example not write to stdio, likewise you will have to consider how the background thread should update the state of the program with out conflicting with updates performed by the main thread (and other background threads).

Writing to stdio can be solved very simply by posting the write action to the GUI thread so that it performs the actual write.

Your example can look like this:

Code: Select all

clauses     onPerform(...) :-         retractall (terminate()),         thread := thread::start(job).   clauses     job() :-         if not (terminate()) then             postAction({ :- stdio::write("performing\n") }), % We post the writing to the GUI thread             job()         end if.
Using deleyCall you can also let the background task run as events in the GUI thread instead:

Code: Select all

clauses     onPerform(...) :-         retractall (terminate()),         delayCall(1, Job).   clauses     job() :-         if not (terminate()) then             stdio::write("performing\n"), % We are in the GUI thread             delayCall(1, Job)         end if.
You will need a delay, otherwise the background task will starve the normal event handling.

Running in the GUI thread like that is much simpler when it comes to accessing and updating the data in the program.
Regards Thomas Linder Puls
PDC
Peter Muraya
VIP Member
Posts: 147
Joined: 5 Dec 2012 7:29

Unread post by Peter Muraya »

Hi Thomas.
Yes, you are right, the piece as I had written would never terminate.

I have used the delayCall method to build and test the solution. It works very well. Here it is, complete with a counter to show me that the job is running.

Code: Select all

class facts      terminate:() determ. predicates     onPerform : window::menuItemListener. clauses     onPerform(Source, _MenuTag):-          /*          Clear the terminate flag*/          retractall (terminate()),          /*          Display the stop dialog modelessly*/          stop::display(Source)=_,            /*          Run the long job*/          delayCall(1, job).    /*  The looooong job*/  facts      counter:varM{integer}:=varM::new(0).  predicates      job:().  clauses      job():-           stdio::write("performing ", counter:value, "\n"),           if                terminate()           then                exception::raise_error("aborted")           else                counter:value := counter:value+1,                delayCall(1, job)           end if.      set_terminate():-assertz(terminate()).  


Under what (special) conditions would you use the thread::start approach with all the conditions it imposes on access to the resources of the main thread?
Mutall Data Management Technical Support
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post by Thomas Linder Puls »

With the delayCall approach each cycle in the job will run in the GUI thread. Therefore it will have to be short otherwise the GUI will become unresponsive.

The real multithreaded approach can run as long as you like without affecting the GUI thread.
Regards Thomas Linder Puls
PDC
Peter Muraya
VIP Member
Posts: 147
Joined: 5 Dec 2012 7:29

Unread post by Peter Muraya »

Thanks, Thomas.
Let me get this right as I think it is important for choosing the best approach. Do you mean that the Do_the_real_work() predicate in the code below would have to be short/fast in the delayCall method to avoid making the main thread unresponsive? And that this constraint does not apply to the thread::start method?

Code: Select all

clauses     /*     Using thread::start method*/     job() :-         if not (terminate()) then             Do_the_real_work(),             job()         end if.         /*     Using the delayCall method*/       job() :-         if not (terminate()) then             Do_the_real_work(),             delayCall(1, Job)         end if.
Mutall Data Management Technical Support
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post by Thomas Linder Puls »

Yes.
Regards Thomas Linder Puls
PDC
Peter Muraya
VIP Member
Posts: 147
Joined: 5 Dec 2012 7:29

Unread post by Peter Muraya »

Thanks Thomas.
I was motivated to look at the multithread package by your thread::start method; there is quite a lot of stuff there! I will have a look at the Language reference to see if I can make sense of it. Thanks.
Mutall Data Management Technical Support
Post Reply