PerlおよびGUI。 ストリームを操作する

非常に痛いテーマであるPerl + GUI +スレッドに触れます。

痛い、あなたのアプリケーションをスレッドで動作させる試みが失敗するかもしれないので。 プログラムは「ハング」、「segfolit」します。ドキュメントを見ると、ライブラリがスレッドセーフではないことがわかります。 排水溝に費やした時間はありましたか?



ヒント:MainLoop()はイベントループを開始し、コードの実行をブロックするため、Tkx :: MainLoopを呼び出す前にスレッドを作成します。 それはとても簡単でしょう! この条件でコードを書き直しましたが、まだハングしています...



どうする? 抜け道があります。

Boss / Workersモデルとメッセージキューを使用する必要があります。



目的:GUIを使用してアプリケーションを作成し、マルチスレッドを使用します。

「指で」問題を見てみましょう。すべてを抽象的なモデルの形で提示します。



倉庫があります。 あなたは上司に来ます

-こんにちは、この小さなリストを集めてください...

-さて、ここでタスクを部分的に分散させます。作業者がすべてを行います。



店主は、山からタスクを受け取ります(そして、受け取った順に受け取ります)。



同様のキューは、 Thread::Queue



パッケージによって実装されます。



いくつかの方法を使用します

-エンキュー-タスクを置く

-デキュー、dequeue_nb-タスクを引き受ける



dequeueとdequeue_nbの違いは、後者がノンブロッキングであることです。



言い換えると、デキューを呼び出すとき、タスクが表示されるまで待機し、その後にのみタスクを取得します。 2番目の場合、ジョブがない場合、undefが返されます。



 while(defined(my $ item = $ queue-> dequeue())){
   #アクションを実行します。
 }




店主が必要なすべての商品を収集したので、今度はローダーがそれを拾ってあなたに届けます。

...



それでは、実装(簡略版)に取り掛かりましょう。



タスク-> Tk->ボス->労働者->結果



画像



 #!/ usr / bin / perl
厳格な使用;

 Tkxを使用します。  #ツールキット

スレッドを使用します。  #スレッドを操作する
スレッドを使用::キュー。  #キューを実装します

 #キューを作成
 my $ queue_tk = Thread :: Queue-> new();  #Tkからタスクを取得
 my $ queue_job = Thread :: Queue-> new();  #従業員に送る
 my $ queue_box = Thread :: Queue-> new();  #結果

 #上司
 sub thread_boss {
    私の$ self = threads-> self();
    私の$ tid = $ self-> tid();
    
     while(defined(my $ item = $ queue_tk-> dequeue())){
         print STDERR "ボス($ tid)はTkからタスクを受け取りました:$ item \ n";
        
         #従業員に仕事を送ります
         $ queue_job-> enqueue($ item);
     }
    
     $ queue_job-> enqueue(undef);
 }

 #従業員
 sub thread_worker {
    私の$ self = threads-> self();
    私の$ tid = $ self-> tid();
    
     while(defined(my $ job = $ queue_job-> dequeue())){
         print STDERR "労働者($ tid)はボスからタスクを受け取りました:$ job \ n";
        
         #いくつかの作業を行う...
         print STDERR "ワーカー($ tid)がタスクを終了しました\ n";
        
         #すべてを1つのボックスに入れます;)
         $ queue_box-> enqueue( "processed:$ job");
     }

     $ queue_box-> enqueue(undef);    
 }
    
 #スレッドを作成
 my $ boss = threads-> new(\&thread_boss);
私の$ worker = threads-> new(\&thread_worker);


 #UIを作成する
 my $ main_window = Tkx :: widget-> new( '。');
私の$ 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);

 #入力フィールド
 my $ entry_data = 'ここにデータを入力してください';
 my $ entry = $ frame-> new_ttk__entry(-textvariable => \ $ entry_data);

 $ button = $ frame-> new_ttk__button(
     -text => 'ボスに送信'、
     -command => sub {
         $ queue_tk-> enqueue($ entry_data);
     }、
 );

 $ entry-> g_grid(-row => 1、-column => 0);
 $ button-> g_grid(-row => 1、-column => 1);

 #イベントハンドラーWM_DELETE_WINDOW
 sub on_destroy {
     my $ mw = shift;

     #スレッドを終了するundefキューを送信します
     $ queue_tk-> enqueue(undef);
     $ queue_box-> enqueue( 'finish');

     #破壊する
     #またはTkx :: destroy( '。')
     $ mw-> g_destroy();
 }

 $ main_window-> g_wm_protocol( 'WM_DELETE_WINDOW'、[\&on_destroy、$ main_window]);

 #結果を処理する
サブモニター{
    私の$ status_lbl = shift;
    私の$ result = $ queue_box-> dequeue_nb;    

     if($ result ne 'finish'){
         if(定義された$結果){
             $ label-> configure(-text => "job completed:" .scalar(localtime));
         }
            
         Tkx :: after(1000、[\&monitor、$ label]);
     }
    
 }

 #監視を開始
 Tkx :: after(100、[\&monitor、$ label]);

 #スレッドを解除する
 #それ以外の場合、プログラムの最後に警告が表示されます
 #Perlはアクティブなスレッドで終了しました:
 #2実行中および未参加
 #0終了し、参加していません
 #0実行中および切り離し
 $ boss-> detach();
 $ worker-> detach();

 Tkx :: MainLoop();




ネットワーク、データベースを操作するためのマルチスレッドプログラムを作成する場合は、標準ストリームの代わりに、 POE (イベントマシン、非ブロッキングソケット)を使用する方がはるかに適切だと思います。



これはドラフトですが、補足されます。






All Articles