Perl and GUI. Work with streams
I will touch on a very sore subject, Perl + GUI + threads.
Sore, because an attempt to get your application to work with threads may fail. The program “hangs”, “segfolit”, you will turn to the documentation, you will see that the library is not thread-safe. Was the time spent down the drain?
Hint: create threads before calling Tkx :: MainLoop, since MainLoop () starts its event loop and blocks code execution. It would be so simple! You rewrote the code with this condition, but it still hangs ...
What to do? There is an exit.
You need to use the Boss / Workers model (Queue and Workers).
Purpose: to write an application with a GUI and use multithreading.
Let’s consider the task “on the fingers”, present everything in the form of an abstract model.
There is a warehouse. You come to the boss (boss),
- Hi, collect this little list for me ...
- Okay, now I will scatter the task in parts, the workers will do everything.
Storekeepers take tasks from the pile (and take them in the order of their receipt).
A similar queue is implemented by a package
We will use several methods
- enqueue - put the task
- dequeue, dequeue_nb - take the task
The difference between dequeue and dequeue_nb is that the latter is non-blocking.
In other words, when we call dequeue, we wait until the task appears, and only then we get it. And in the second case, if there is no job, then undef is returned.
The storekeepers collected all the necessary goods, now the loader will pick it up and bring it to you.
...
Now let's start implementation (simplified version).
Task -> Tk -> Boss -> Worker -> Result
If you plan to write a multi-threaded program for working with a network, databases, then I think that instead of standard streams, it would be much more correct to use POE (event machine, non-blocking sockets).
While this is a draft, it will be supplemented.
Sore, because an attempt to get your application to work with threads may fail. The program “hangs”, “segfolit”, you will turn to the documentation, you will see that the library is not thread-safe. Was the time spent down the drain?
Hint: create threads before calling Tkx :: MainLoop, since MainLoop () starts its event loop and blocks code execution. It would be so simple! You rewrote the code with this condition, but it still hangs ...
What to do? There is an exit.
You need to use the Boss / Workers model (Queue and Workers).
Purpose: to write an application with a GUI and use multithreading.
Let’s consider the task “on the fingers”, present everything in the form of an abstract model.
There is a warehouse. You come to the boss (boss),
- Hi, collect this little list for me ...
- Okay, now I will scatter the task in parts, the workers will do everything.
Storekeepers take tasks from the pile (and take them in the order of their receipt).
A similar queue is implemented by a package
Thread::Queue
. We will use several methods
- enqueue - put the task
- dequeue, dequeue_nb - take the task
The difference between dequeue and dequeue_nb is that the latter is non-blocking.
In other words, when we call dequeue, we wait until the task appears, and only then we get it. And in the second case, if there is no job, then undef is returned.
while (defined (my $ item = $ queue-> dequeue ())) { # perform any actions. }
The storekeepers collected all the necessary goods, now the loader will pick it up and bring it to you.
...
Now let's start implementation (simplified version).
Task -> Tk -> Boss -> Worker -> Result
#! / usr / bin / perl use strict; use Tkx; # toolkit use threads; # work with threads use Thread :: Queue; # implements a queue # create queues my $ queue_tk = Thread :: Queue-> new (); # get tasks from Tk my $ queue_job = Thread :: Queue-> new (); # send to employees my $ queue_box = Thread :: Queue-> new (); # results #boss sub thread_boss { my $ self = threads-> self (); my $ tid = $ self-> tid (); while (defined (my $ item = $ queue_tk-> dequeue ())) { print STDERR "Boss ($ tid) has received the task from Tk: $ item \ n"; # send the job to the employee $ queue_job-> enqueue ($ item); } $ queue_job-> enqueue (undef); } # employee (s) sub thread_worker { my $ self = threads-> self (); my $ tid = $ self-> tid (); while (defined (my $ job = $ queue_job-> dequeue ())) { print STDERR "Worker ($ tid) has received task from Boss: $ job \ n"; # do some work ... print STDERR "Worker ($ tid) has finished the task \ n"; # we drop everything in one box;) $ queue_box-> enqueue ("processed: $ job"); } $ queue_box-> enqueue (undef); } # create threads my $ boss = threads-> new (\ & thread_boss); my $ worker = threads-> new (\ & thread_worker); # Create a UI my $ main_window = Tkx :: widget-> new ('.'); my $ frame = $ main_window-> new_ttk__frame (-padding => q / 10 10 10 10 /); $ frame-> g_grid (); my $ label = $ frame-> new_ttk__label (-text => 'waiting'); $ label-> g_grid (-row => 0, -column => 0, -columnspan => 2); # input field my $ entry_data = 'enter data here'; my $ entry = $ frame-> new_ttk__entry (-textvariable => \ $ entry_data); my $ button = $ frame-> new_ttk__button ( -text => 'Send to Boss', -command => sub { $ queue_tk-> enqueue ($ entry_data); }, ); $ entry-> g_grid (-row => 1, -column => 0); $ button-> g_grid (-row => 1, -column => 1); # event handler WM_DELETE_WINDOW sub on_destroy { my $ mw = shift; # send undef queues, which will terminate the threads $ queue_tk-> enqueue (undef); $ queue_box-> enqueue ('finish'); # Destroy # or Tkx :: destroy ('.') $ mw-> g_destroy (); } $ main_window-> g_wm_protocol ('WM_DELETE_WINDOW', [\ & on_destroy, $ main_window]); # process the result sub monitor { my $ status_lbl = shift; my $ result = $ queue_box-> dequeue_nb; if ($ result ne 'finish') { if (defined $ result) { $ label-> configure (-text => "job completed:" .scalar (localtime)); } Tkx :: after (1000, [\ & monitor, $ label]); } } # start monitoring Tkx :: after (100, [\ & monitor, $ label]); # unfasten threads # otherwise, at the end of the program, we will have warnings # Perl exited with active threads: # 2 running and unjoined # 0 finished and unjoined # 0 running and detached $ boss-> detach (); $ worker-> detach (); Tkx :: MainLoop ();
If you plan to write a multi-threaded program for working with a network, databases, then I think that instead of standard streams, it would be much more correct to use POE (event machine, non-blocking sockets).
While this is a draft, it will be supplemented.