25

を検索するDoEventsだけで、基本的に次のような結果が得られます。

DoEvents悪です。使用しないでください。代わりにスレッドを使用してください。

一般的に引用される理由は次のとおりです。

  • 再入可能性の問題
  • 業績不振
  • 使いやすさの問題(無効なウィンドウ上でのドラッグアンドドロップなど)

TrackPopupMenuただし、およびなどのいくつかの注目すべきWin32関数は、UIの応答性を維持するためにDoDragDrop 、独自のメッセージ処理を実行しDoEventsます。
それでも、これらの問題(パフォーマンス、再入可能性など)に遭遇することはないようです。

どうやってやっているの?で引用された問題をどのように回避しDoEventsますか?(または彼らは?

4

3 に答える 3

31

DoEvents()は危険です。しかし、私はあなたが毎日たくさんの危険なことをしているに違いない。ちょうど昨日、私はいくつかの爆発装置を始動させました(将来の読者:特定のアメリカの休日に関連する元の投稿日をメモしてください)。注意して、私たちは時々危険を説明することができます。もちろん、それは危険が何であるかを知り、理解することを意味します:

  • 再入国の問題。ここには実際には2つの危険があります。

    1. ここでの問題の一部は、コールスタックに関係しています。DoEvents()などを使用するメッセージを処理するループで.DoEvents()を呼び出すと、かなり深い呼び出しスタックが得られます。DoEvents()を使いすぎて、誤って呼び出しスタックがいっぱいになり、StackOverflow例外が発生するのは簡単です。1つまたは2つの場所で.DoEvents()のみを使用している場合は、おそらく問題ありません。長時間実行するプロセスがあるときはいつでも、それが最初に手が届くツールである場合は、ここで簡単に問題を見つけることができます。間違った場所で1回使用しただけでも、ユーザーがスタックオーバーフロー例外を強制する可能性があり(場合によってはEnterキーを押したままにするだけ)、セキュリティ上の問題になる可能性があります。
    2. コールスタックで同じメソッドを2回見つけることが可能な場合があります。これを念頭に置いてメソッドを作成しなかった場合(ヒント:おそらく作成しなかったでしょう)、悪いことが起こる可能性があります。メソッドに渡されるすべてが値型であり、メソッドの外部のものに依存しない場合は、問題ない可能性があります。ただし、それ以外の場合は、.DoEvents()が呼び出された時点で制御が返される前に、メソッド全体が再度実行された場合にどうなるかを慎重に検討する必要があります。メソッド外のどのパラメーターまたはリソースが、予期していなかったように変更される可能性がありますか?メソッドは、スタック上の両方のインスタンスが同じオブジェクトに作用している可能性があるオブジェクトを変更しますか?
  • パフォーマンスの問題。DoEvents()はマルチスレッドのような錯覚を与える可能性がありますが、実際のマルチスレッドではありません。これには、少なくとも3つの実際の危険があります。

    1. DoEvents()を呼び出すと、既存のスレッドの制御がメッセージポンプに戻されます。メッセージポンプは、他の何かを制御する可能性があり、他の何かには時間がかかる可能性があります。その結果、元の操作は、それ自体が制御を生成しないスレッド内にある場合よりも、終了するのにはるかに長い時間がかかる可能性があります。これは、必要以上に確実に長くなります。
    2. 仕事の重複。同じメソッドを2回実行していることに気付く可能性があり、前述のすべての外部依存関係を考慮したとしても、このメソッドはコストがかかる/長時間実行される(または最初からDoEvents()は必要ない)ことはすでにわかっているためです。上記のように有害な副作用がないので、それでも多くの作業を複製してしまう可能性があります。
    3. もう1つの問題は、最初の問題の極端なバージョンです。デッドロックの可能性です。プログラム内の他の何かがプロセスの終了に依存し、それが完了するまでブロックし、それがDoEvents()からのメッセージポンプによって呼び出された場合、アプリはスタックして応答しなくなります。これはとてつもなく聞こえるかもしれませんが、実際には、偶然に行うのは驚くほど簡単であり、クラッシュを見つけて後でデバッグするのは非常に困難です。これは、自分のコンピューターで経験した可能性のあるハングしたアプリの状況の根本にあります。
  • 使いやすさの問題。これらは、他の危険を適切に考慮していないことに起因する副作用です。他の場所を適切に見ている限り、ここで新しいことは何もありません。

これらすべてのことを確実に説明できる場合は、先に進んでください。しかし実際には、DoEvents()がUIの応答性/更新の問題を解決するために最初に探す場所である場合、おそらくこれらの問題のすべてを正しく考慮しているわけではありません。それがあなたが最初に見る場所ではない場合、DoEvents()を検討することにどのように到達したのか疑問に思うほど他のオプションが十分にあります。現在、DoEvents()は主に、他の信頼できるオプションが利用可能になる前に登場した古いコードとの互換性のために、また他のオプションに触れるための十分な経験をまだ得ていない新しいプログラマーのための松葉杖として存在します。

現実には、少なくとも.Netの世界では、ほとんどの場合、BackgroundWorkerコンポーネントはほぼ同じくらい簡単で、少なくとも1回または2回実行すると、安全な方法で機能します。最近では、async / awaitパターンまたはaの使用は、Task本格的なマルチスレッドコードを自分で調べる必要なしに、はるかに効果的かつ安全になります。

于 2012-07-05T21:07:02.130 に答える
4

16ビットWindowsの時代に戻ると、すべてのタスクが単一のスレッドを共有していたとき、タイトなループ内でプログラムの応答性を維持する唯一の方法はでしたDoEvents。スレッドを支持することをお勧めしないのは、この非モーダルな使用法です。典型的な例を次に示します。

' Process image
For y = 1 To height
    For x = 1 to width
        ProcessPixel x, y
    End For
    DoEvents ' <-- DON'T DO THIS -- just put the whole loop in another thread
End For

モーダルなもの(ポップアップの追跡など)の場合は、それでも問題ない可能性があります。

于 2012-07-05T20:54:10.953 に答える
3

私は間違っているかもしれませんが、UIを引き継ぐという点で、かなり特殊なケースであるように思われるので、再入可能性の問題はありません(これが人々が「悪」と表現する主な理由だと思いますDoDragDrop)。TrackPopupMenuDoEvents

個人的には、機能を「悪」として却下することは役に立たないと思います。むしろ、人々が自分で決めることができるように落とし穴を説明してください。DoEventsモーダルプログレスダイアログが表示されているときなど、ユーザーがUIの残りの部分と対話できないため、再入可能性の問題がない場合など、それを使用することが依然として合理的であるというまれなケースがあります。

もちろん、「悪」とは「落とし穴を完全に理解せずに使用してはならないもの」を意味するのであれば、それDoEventsは悪であることに同意します。

于 2012-07-05T21:04:33.983 に答える