[C#] WPF アプリケーションで時間のかかる処理を実行すると、COM コンテキストの移行ができないエラーが発生する
このページのタグ:[C#] [WPF]
WPF アプリケーションで時間のかかる処理を実行すると、COM コンテキストの移行ができないエラーが発生する現象について紹介します。

現象

WPFアプリケーションで時間のかかる処理を実行すると、下記のエラーが発生することがあります。
マネージ デバッグ アシスタント 'ContextSwitchDeadlock' が 'C:\(事項ファイルのパス)\(実行ファイル名).vshost.exe' で問題を検出しました。
追加情報:CLR は、COM コンテキスト 0xnnnnnn から COM コンテキスト 0xnnnnnn へ 60 秒で移行できませんでした。ターゲット コンテキストおよびアパートメントを所有するスレッドが、ポンプしない待機を行っているか、Windows のメッセージを表示しないで非常に長い実行操作を処理しているかのどちらかです。この状態は通常、パフォーマンスを低下させたり、アプリケーションが応答していない状態および増え続けるメモリ使用を導く可能性があります。この問題を回避するには、すべての Single Thread Apartment (STA) のスレッドが、CoWaitForMultipleHandles のようなポンプする待機プリミティブを使用するか、長い実行操作中に定期的にメッセージをポンプしなければなりません。

再現プログラム

以下のプログラムで再現できます。

UI

下図のUIを作成します。ButtonとTextBoxをフォームに配置します。

コード

下記のコードを記述します。[Button1]のClickイベントを実装します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfMessagePump
{
  /// <summary>
  /// MainWindow.xaml の相互作用ロジック
  /// </summary>
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
    }

    private void button_Click(object sender, RoutedEventArgs e)
    {
      for (int i = 0; i < 1000000; i++) {
        System.Threading.Thread.Sleep(200);
        textBox.Text += "Message:" + Convert.ToString(i) + "\r\n";
      }
    }
  }
}

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。[Button]をクリックすると画面がロックします。


1分ほど経過すると、先に紹介した、下図のエラーが表示されます。

対策

対策1:DispatcherFrameを利用して明示的にイベントを処理する

DispatcherFrameを利用して明示的にイベントを処理することで、アプリケーションの応答を処理することで、フリーズ状態を避け、エラーを防ぐことができます。コードの詳細については「[C#] コントロールを明示的に更新する - WPFアプリケーションでのApplication.DoEvents の実装」の記事を参照してください。

対策2:Taskを利用する

Taskクラスを利用して、別スレッドで並列処理することで、UIスレッドのロックを防げます。WPFアプリケーションでのTaskの利用についてはこちらの記事を参照してください。

対策3:非同期関数 async await を利用する

対策2のTaskを利用する方式と同様にTaskを利用しますが、非同期関数と組み合わせてUIスレッドのロックを防ぐ方法です。実装の詳細はこちらの記事を参照して下さい。

プライバシー    iPentecについて
iPentec all rights reserverd. (ISDC)