2011-01-11 20 views
1

私はインターフェイスをフリーズしているc#.NETマルチスレッドアプリケーションがあります。私がシステムをアイドル状態にしてからスクリーンセーバーを起動させないと、インターフェイスがフリーズしない(システムにアクセスするためにパスワードを再入力する必要がある)というのは珍しいことです。インターフェイスが再び表示されると(パスワードを正常に入力した後)、インターフェイスはロックされます。私がスクリーンセーバーを始動させない限り、インターフェースはロックアップしません。マルチスレッドのC#アプリケーションでインターフェイスがフリーズする

私は、同じDLLにアクセスする2つの異なる実行可能ファイルがあり、DLLにアクセスするためにどのアプリケーションを使用してもこの問題が発生していることを指摘しておきます。これは、DLLに関連する方法とは別に、2つのアプリケーションが完全に異なっている(C++/MFC)と(C#/ .NET)という問題がDLLにあることを暗示しているようです。

両方のexeは、DLLとのやり取りにおいて同様の手順を実行します。彼らはシリアルポートの通信をセットアップするためのDLLへの呼び出しを行い、DLLのステータスウィンドウを開き、DLLのスレッドを開始して通信ポートを監視し、dllのスタックを監視するメインアプリでスレッドを開始します。

データがcommポートからDLLのスレッドによって取得されると、解析され、結果がスタックに配置され、デリゲートを介してステータスウィンドウにポストされます。 exe内のスレッドがスタック内のデータを見ると、デリゲートを使用してメインウィンドウにデータを出力します。

DLL内のスレッドにコードを追加してApplication.DoEvents()を30秒ごとに呼び出すと、インターフェイスが約30秒間フリーズして通常のように再開することが判明しました。 何かがメインスレッドをブロックしているとわかり、DoEvents()が強制的にロックを解除するように見えますが、何が原因でこのロックが解除されたのかはわかりません。

この問題は、開発マシンとテストマシンの両方で発生します。

私は、DLL内のステータスウィンドウにデータの出力を完全に削除しようとしましたが、違いはありませんでした。

私は長年にわたってマルチスレッドプログラミングを行ってきましたが、このようなことはまったくありませんでした。どんなアドバイスも大歓迎です。

ありがとうございました。

+3

デバッガでフリーズしたUIを一時停止し、コールスタックを確認します。 – SLaks

+0

DLLメソッドをUI以外のスレッドからのみ呼び出し、UIスレッドにマーシャリングしてみましたか? – RobS

+0

これは完全に役立つわけではないかもしれませんが、スクリーンセーバーが起動したときに起こる省電力スイッチがありますか? (ポートがスリープ状態になるかもしれないと思っています) – vlad259

答えて

6

これは、非標準的な方法でユーザーインターフェイスを初期化するときに、SystemEventsクラスによって一般的に引き起こされる問題です。特にスレッドを使用する。あなたのプログラム、Debug + Break All、Debug + Windows + Threadsを起動してください。 ".NET SystemEvents"という名前のスレッドが見つかった場合、このハングアップが発生する可能性がかなりあります。

背景:SystemEventクラスは、コンソールモードのアプリケーションとGUIアプリケーションの両方をサポートしています。後者の場合、イベントハンドラはUIスレッドで起動する必要があります。イベントの最初の1つが購読されると、システム通知を取得するための少し目立たないヘルパーウィンドウが作成されます。これは、呼び出しスレッド上にウィンドウを作成するか、ヘルパースレッドを起動することによって、この2つの方法で行うことができます。 Thread.GetApartmentState()の値に基づいて決定します。それがSTAならば、呼び出し元のスレッドにウィンドウを作成することができ、すべてのイベントコールバックをそのスレッドに適切にマーシャリングすることができます。

最初に作成するウィンドウがUIスレッドで作成されていないと、これは間違っています。たとえば、スプラッシュ画面。そのウィンドウには、UserPreferenceChangedのようなシステムイベントに関心のあるコントロールが含まれているため、正しく再描画できます。これでヘルパースレッドが使用され、UIスレッドではなく、そのヘルパースレッドからイベントが発生します。 UIスレッドで実行される任意のウィンドウへのポイズン。ロックされたワークステーション(スクリーンセーバーを含む)からのセッション切り替えは、デッドロックを引き起こす可能性があるいくつかの不思議な理由によるものです。あなたはまた、間違った絵の事故を見るかもしれません。

初期化順序を固定からショート、回避策はどのウィンドウが作成される前に、自分のメイン()メソッドでこれを置くことです:

Microsoft.Win32.SystemEvents.UserPreferenceChanged += delegate { }; 
+0

私はこのDLLを呼び出すC#/ .NETアプリケーションにこれを簡単に追加できますが、このDLLを呼び出すC++/MFCアプリケーションでは何をしますか? – PhilGyro

+0

Erm、初期化関数を呼び出しますか? C++コードはスレッド(CoInitializeExをSTAを選択する)を正しく初期化しますか?そのinit関数でThread.CurrentThread.GetApartmentState()をチェックする方が良いでしょう。 –

+0

DLLを最初に入力したときに、現在のスレッドのアパートメント状態を確認しました。これはSTA用に設定されています。私はまたあなたの上記の行をApplication.Run()の前に私のC#exeのmainメソッドに追加しましたが、それは何の違いもありませんでした。私は私のexeを起動すると、私はメインフォームのウィンドウを開き、ユーザーがボタンをクリックすると、COMポートを開いてスレッドを開始するDLLに呼び出しが行われます。ユーザーが実行するモードを選択すると、exeはDLLに別の呼び出しを行い、ステータスウィンドウを開き、dllのスレッドにデータの収集を開始するよう警告します。 – PhilGyro

0

問題は、ActiveXコントロールに関連すると思われるんおそらくフォームで間違って使用されていました。私は.NETでシリアルポートライブラリを使用するように切り替えて、私の問題を再現できませんでした。みんな、特にハンスのおかげで彼らの支援に感謝します。

0

スクリーンセーバーが起動したり、PCがロックされてモニタがスリープ状態になると、私のPCと同じ問題が発生します。 マルチスレッドアプリケーションにデッドロックが表示されていることを95%確信しています。コード内にデッドロックがあるかどうかを調べ、識別します。

関連する問題