C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

非同期処理実行時のApplication.Idleイベント

[トピック内 13 記事 (1 - 13 表示)]  << 0 >>

■89268 / inTopicNo.1)  非同期処理実行時のApplication.Idleイベント
  
□投稿者/ コリオリ (1回)-(2018/11/15(Thu) 09:47:02)

分類:[VB.NET/VB2005 以降] 

(使用言語:VB2013)

例えば、処理Aを行うと、UI上では色々とイベントが発生します。
その後に処理Bを行いたいのですが、上記イベント処理が終了して
UIの動作が落ち着いたところで、処理Bを実行したいのです。
この時にApplication.Idleを使用していますが、
具体的な方法として、処理AでApplication.IdleをAddHandlerし、
Application.Idleイベントが発生したら処理Bを呼び出し、
Application.IdleをRemoveHandlerします。

ここで一つ疑問に思ったのが、Application.Idleは別スレッドの動作も含めて
アイドル状態となった時に発生するのでしょうか?
今後、BackgroundWorkerを使用した非同期処理を考えていて(勉強中)、
もしBackgroundWorkerの動作がApplication.Idleに影響するのであれば、
上記の例でApplication.Idleを使用すると、場合によっては
何時まで経っても処理Bが動作しない、ということがあり得る、と思ったからです。
もし、上記問題が発生する場合、どのような方法でUIスレッドのアイドル状態を
検出すればよいでしょうか?
引用返信 編集キー/
■89270 / inTopicNo.2)  Re[1]: 非同期処理実行時のApplication.Idleイベント
□投稿者/ kiku (57回)-(2018/11/15(Thu) 10:26:34)

> ここで一つ疑問に思ったのが、Application.Idleは別スレッドの動作も含めて
> アイドル状態となった時に発生するのでしょうか?

Application.Idleがどのような条件で発生するのかに関して
正確な情報を知りません。
わたしの理解は、下記であるとして回答します。

わたしの考え:
 Application.Idleは、別スレッドも含めて
 OSがCPUのリソースを割り当てるスレッドがどこにもないときに
 発生するものと考えています。

上記のようなことを仮定すると、処理Aが終わってから処理Bを
確実に行わせたいのであるならば、
Application.Idleは使うべきではないと考えます。

Application.Idleで処理Bを実施した場合、
通常状態ではたぶん問題ないと思いますが
該当するアプリ以外の処理が混みあってくると
Application.Idleが発生しなくなり、
処理Bが処理されなくなることが容易に予想されます。

処理Aが確実に終わったとする状態や、イベントを実装するのが筋が良いと思います。


引用返信 編集キー/
■89271 / inTopicNo.3)  Re[1]: 非同期処理実行時のApplication.Idleイベント
□投稿者/ 774RR (646回)-(2018/11/15(Thu) 10:39:18)
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.application.idle
こっちの解説には詳細がないっすね (英語に切り替えて読むべし) 。

https://stackoverflow.com/questions/10611924/
この辺には「待機している(ウインドウ)メッセージがなくなったら」とあるので
バックグラウンド処理の云々は関係なくて UI メッセージキューが空になったら、で良いらしい。


引用返信 編集キー/
■89272 / inTopicNo.4)  Re[1]: 非同期処理実行時のApplication.Idleイベント
□投稿者/ 魔界の仮面弁士 (1937回)-(2018/11/15(Thu) 11:05:40)
2018/11/15(Thu) 16:33:48 編集(投稿者)

No89268 (コリオリ さん) に返信
> 今後、BackgroundWorkerを使用した非同期処理を考えていて(勉強中)、

BackgroundWorker を使う場合の方針として:

(1) DoWork 内では、ローカル変数のみを使う。コントロールの読み書きは御法度。

(2) DoWork 内で、Form のフィールド変数などにアクセスするのは避ける。
  BeginInvoke / Invoke などでのコントロール操作も極力避ける。

(3) 進捗表示が必要なら、ReportProgress メソッドと ProgressChanged イベントを使う。

(4) BackgroundWorker が稼働中に、呼び出し元フォームが終了することの是非も検討しておく。



> ここで一つ疑問に思ったのが、Application.Idleは別スレッドの動作も含めて
> アイドル状態となった時に発生するのでしょうか?

Idle を呼び出すのは UI スレッドの役目であり、他のスレッドは直接影響しません。
現在の UI スレッドのメッセージキューが空になった時にのみ発生します。
「空の時」ではなく、「空になった時」である点が肝。

新しい Windows メッセージが来ていない場合には、現在アイドル状態で
あったとしても、Idle イベントは発生しないのでご注意を。


--- 以下蛇足 ---


メッセージループを回す個所は幾つかありますが、その一つがモーダル ダイアログ。
たとえば ShowDialog メソッドを呼び出した場合、最終的には、
 Const msoloopModalForm As Intger = 4
 Application.ThreadContext.FromCurrent().RunMessageLoop(msoloopModalForm, コンテキスト)
というコードに行きつき、ここでメッセージループが回されることになります。


VB のスタートアップ フォームはモーダルではありませんが、これもメッセージループが処理されています。

スタートアップフォームは基本的に、System.Windows.Forms.Application.Run メソッドから呼ばれます。
もう少し具体的に言えば、
 System.Windows.Forms.Application.Run( ApplicationContext )
もしくは
 System.Windows.Forms.Application.Run( Form )
のいずれかから呼び出されるということです。
(C# プロジェクトだと、Program.cs 内の static void Main() に上記の記述があります)

そしてこの Application.Run メソッドが
 Const msoloopMain As Intger = -1
 Application.ThreadContext.FromCurrent().RunMessageLoop(msoloopMain, コンテキスト)
のようにして、メッセージ ループを回しています。


VB だと、既定でアプリケーション フレームワークが有効になっているため、
Application.Run という記述は見えないのですが、コンパイル時に自動的に
 Public Sub Main(commandLine As String())
  Application.SetCompatibleTextRenderingDefault(bool)
  My.Application.Run(commandLine)
 End Sub
に相当する処理が実装されます。

そして上記の、My.Application.Run メソッドからは、先述の
 System.Windows.Forms.Application.Run( ApplicationContext )
が呼び出されているという流れです。


ちなみに、このアプリケーション フレームワークからの処理の流れは、
 Public Class Form1
  Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
   Me.TopMost = True
   Me.Close()
  End Sub
 End Class
というコードを書いてデバッグ実行すると見えてきます。


上記をデバッグ実行すると、ObjectDisposedException 例外で停止してしまうのですが、
その時のスタック トレースに、My.Application.Run(commandLine) からの流れが記載されています。

場所 System.Windows.Forms.Control.CreateHandle()
場所 System.Windows.Forms.Form.CreateHandle()
場所 System.Windows.Forms.Control.get_Handle()
場所 System.Windows.Forms.ContainerControl.FocusActiveControlInternal()
場所 System.Windows.Forms.Form.SetVisibleCore(Boolean value)
場所 System.Windows.Forms.Control.set_Visible(Boolean value)
場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
場所 Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()
場所 Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()
場所 Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)
場所 WindowsApp1.My.MyApplication.Main(String[] Args) ()
引用返信 編集キー/
■89273 / inTopicNo.5)  Re[2]: 非同期処理実行時のApplication.Idleイベント
□投稿者/ コリオリ (2回)-(2018/11/15(Thu) 11:19:30)
皆様、回答ありがとうございます。

> https://stackoverflow.com/questions/10611924/
> この辺には「待機している(ウインドウ)メッセージがなくなったら」とあるので
> バックグラウンド処理の云々は関係なくて UI メッセージキューが空になったら、で良いらしい。

確認してみます。
試しに、非常に長い処理をバックグラウンドで処理させた状態で、UI側でApplication.Idleがどうなるか確認してみます。
(すぐには試せないのですが、確認後ここに結果を報告します。)
引用返信 編集キー/
■89274 / inTopicNo.6)  Re[2]: 非同期処理実行時のApplication.Idleイベント
□投稿者/ コリオリ (3回)-(2018/11/15(Thu) 11:37:26)
No89272 (魔界の仮面弁士 さん) に返信

先程の書き込みと入れ違いになってしまいました。
色々と情報をくださり、ありがとうございます。
内容をじっくりと読んでいきたいと思います。

ところで、Application.Idleイベントの発生条件で良く分からないことがあります。

> 現在の UI スレッドのメッセージキューが空になった時にのみ発生します。
> 「空の時」ではなく、「空になった時」である点が肝。
>
> 新しい Windows メッセージが来ていない場合には、現在アイドル状態で
> あったとしても、Idle イベントは発生しないのでご注意を。

Application.Idleが発生する条件は、メッセージキューが空になった時であり、
元々メッセージキューの中が空っぽの場合にはApplication.Idleは発生しない、
という所までは理解しました。

ただ、そもそも「メッセージ」とは何か、漠然としてうまく理解できていません。
メッセージとはどのようなものと考えればよいでしょうか?
何らかのイベント内の処理はメッセージには関係ないのでしょうか?
引用返信 編集キー/
■89275 / inTopicNo.7)  Re[3]: 非同期処理実行時のApplication.Idleイベント
□投稿者/ kiku (58回)-(2018/11/15(Thu) 11:38:43)
評価の方法ですが、わたしだったら下記を注意点とします。
 1.同一アプリ内のバックグラウンド処理が重い場合
 2.別アプリが重い場合
 3.処理Aを処理中に、Application.Idleが発生しないか?

たぶん、上記3を発生することがあると思っています。
発生しても、処理Bを実行しないようにするために、
処理Aが終わったか判断するための
仕組みが必要になるのではないかと思っています。

たぶん、それが難しいからApplication.Idleを使おうとしているのだと
思いますが。。。。
引用返信 編集キー/
■89276 / inTopicNo.8)  Re[4]: 非同期処理実行時のApplication.Idleイベント
□投稿者/ コリオリ (4回)-(2018/11/15(Thu) 11:50:36)
No89275 (kiku さん) に返信

ご回答ありがとうございます。
今の所、処理A・処理Bを含むアプリにはバックグラウンド処理はなく、
今後予定しているバックグラウンド処理は、処理A・Bとは全く関係ないものです。
全く無関係だけれど、無関係のバックグラウンド処理がApplication.Idleに影響するか、
というのがボクの疑問点です。

説明が足りず申し訳ございません。
引用返信 編集キー/
■89277 / inTopicNo.9)  Re[5]: 非同期処理実行時のApplication.Idleイベント
□投稿者/ kiku (59回)-(2018/11/15(Thu) 12:00:29)
しつこくてすみません。
当方の思いが伝わっているのか疑問であったので
念のため解説しておきます。
該当しないのであるならば良いので無視してください。

たとえば、処理Aを実施すると下記のようなことを想定しました。

1.処理A開始
2.UIイベント発生(処理Aに関わるもの)
3.UIイベント発生(処理Aに関わるもの)
4.UIイベント発生(処理Aに関わるもの)
5.UIイベント発生(処理Aに関わるもの)
6.処理A終了

例えば、上記3と4の間に、Application.Idleが発生しないか
が気になっています。

引用返信 編集キー/
■89278 / inTopicNo.10)  Re[6]: 非同期処理実行時のApplication.Idleイベント
□投稿者/ コリオリ (5回)-(2018/11/15(Thu) 12:15:18)
No89277 (kiku さん) に返信
> しつこくてすみません。

いえいえ、そんなことはありません。
以下の点は非常に大事なことだと思っています。

> 例えば、上記3と4の間に、Application.Idleが発生しないか
> が気になっています。

アプリケーションのアイドルとは何なのか、分からなくなってきました。
このため、「メッセージとは何か?」という自分の知らない世界を目の当たりにして
自分の無知を恥じる次第です。orz
引用返信 編集キー/
■89282 / inTopicNo.11)  Re[7]: 非同期処理実行時のApplication.Idleイベント
□投稿者/ とっちゃん (552回)-(2018/11/15(Thu) 13:48:23)
No89278 (コリオリ さん) に返信
> このため、「メッセージとは何か?」という自分の知らない世界を目の当たりにして

解決するわけではありませんが
一言で言えば、「メッセージとは、Windowsの動く仕組みを支えるもの」というところですかね。

一応公式資料を貼っておきますね。
https://docs.microsoft.com/ja-jp/windows/desktop/winmsg/windowing

まぁ端的には.NET のイベントと呼ばれるものの大半がメッセージを置き換えていると思っていいです。

引用返信 編集キー/
■89283 / inTopicNo.12)  Re[3]: 非同期処理実行時のApplication.Idleイベント
□投稿者/ 魔界の仮面弁士 (1938回)-(2018/11/15(Thu) 14:05:28)
2018/11/15(Thu) 14:09:00 編集(投稿者)

No89274 (コリオリ さん) に返信
> ただ、そもそも「メッセージ」とは何か、漠然としてうまく理解できていません。
> メッセージとはどのようなものと考えればよいでしょうか?

細かいことを省いてざっくりと言えば、.NET Framework 的には
「System.Windows.Forms.Message」でやりとりされる情報の事です。
Win32 API だと、SendMessage とか PostMessage とかで送出されてたりします。


たとえば、コントロールを貼っていない空のフォームを用意して

Public Class Form1
 Protected Overrides Sub WndProc(ByRef m As Message)
  Debug.WriteLine(m.ToString())
  MyBase.WndProc(m)
 End Sub
End Class


上記フォームをデバッグ実行して、
移動・リサイズ・最小化・マウス操作・キーボード操作などを
行ってみてください。

[イミディエイト] もしくは [出力] ウィンドウのいずれかに、
メッセージの内容が出力されることでしょう。



TextBox クラスの Modified プロパティが返す Boolean は、
自身に EM_GETMODIFY (=0xB8) メッセージを送出することで得ていますし、
Process クラスの CloseMainWindow メソッドは、
ターゲットのウィンドウハンドルに対して
WM_CLOSE (=0x10) メッセージを PostMessage することで実装されています。



> 何らかのイベント内の処理はメッセージには関係ないのでしょうか?

どういう処理を想定しているのかにもよりますが、とりあえず、
関係するものもあれば、関係しないものもありますね。

Button1 の Click イベント内で If TextBox1.Modified Then を呼び出せば、
先述の通り、TextBox に対して EM_GETMODIFY が送出されます。

通知メッセージによってイベントが発生する例としては、たとえばタイマー。
System.Windows.Forms.Timer の Tick イベントは、TimerNativeWindow に
WM_TIMER という通知メッセージが飛んでくることによって呼び出されます。
引用返信 編集キー/
■89286 / inTopicNo.13)  Re[4]: 非同期処理実行時のApplication.Idleイベント
□投稿者/ コリオリ (6回)-(2018/11/16(Fri) 11:38:13)
皆様、回答ありがとうございます。
もう少し、色々と勉強してみます。
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ