2

RSS フィードから最新のニュース ヘッドラインを取得するサービスで、WPF FormattedText オブジェクトを使用してテキスト サイズを決定します。取得するテキストは、指定されたキャンバス サイズである必要があります。サービスは 10 秒ごとにコードを実行し、それよりも時間がかかる場合は最大 2 つのスレッドを使用します。私は TaskFactory を使用しています (指定したスレッドの量に制限するために LimitedConcurrencyLevelTask​​Scheduler をオーバーライドしました)。

これはうまく機能しますが、数日後 (長さは可変)、次の例外が発生し始めます。TPL を使用してマルチスレッド化する前は、同じコードが正常に機能していました。

これが何によって引き起こされているのかを理解する助けが必要です。私が調べているいくつかの考えは次のとおりです: TTF ファイルに保持されているスレッドの衝突、メモリの問題、ディスパッチャ (スタック トレースを参照) が TaskFactory とうまく連携していない、その他?? プロファイリングのセットアップは適切ではありませんが、例外が発生しているときにタスク マネージャーを調べたところ、メモリ使用量は正常に見えました。私の次の試みは、TextBlock オブジェクトを使用して、例外が回避されるかどうかを確認することです。

エラー メッセージ: 指定されたファイルが見つかりません エラー ソース: WindowsBase エラー ターゲット サイト: UInt16 RegisterClassEx(WNDCLASSEX_D)

例外スタック トレース:

MS.Win32.UnsafeNativeMethods.RegisterClassEx(WNDCLASSEX_D wc_d) で MS.Win32.HwndWrapper..ctor(Int32 classStyle、Int32 スタイル、Int32 exStyle、Int32 x、Int32 y、Int32 幅、Int32 高さ、文字列名、IntPtr 親、HwndWrapperHook [] フック) で System.Windows.Threading.Dispatcher..ctor() で System.Windows.Threading.Dispatcher.get_CurrentDispatcher() で System.Windows.Media.TextFormatting.TextFormatter.FromCurrentDispatcher(TextFormattingMode textFormattingMode) で System.Windows. Media.FormattedText.LineEnumerator..ctor(FormattedText text) で System.Windows.Media.FormattedText.DrawAndCalculateMetrics(DrawingContext dc, Point drawingOffset, Boolean getBlackBoxMetrics) で System.Windows.Media.FormattedText.get_Metrics() で (ループ内の FormattedText)

   private static Size GetTextSize(string txt, Typeface tf, int size)
    {
        FormattedText ft = new FormattedText(txt, new CultureInfo("en-us"), System.Windows.FlowDirection.LeftToRight, tf, (double)size, System.Windows.Media.Brushes.Black, null, TextFormattingMode.Display);
        return new Size { Width = ft.WidthIncludingTrailingWhitespace, Height = ft.Height };
    }

編集:これまでのところ、この関数を呼び出すコードの周りにロックを配置し、次のように CurrentDispatcher.Invoke メソッド内で呼び出すことを試みました。

 return (Size)Dispatcher.CurrentDispatcher.Invoke(new Func<Size>(() =>
    {
      FormattedText ft = new FormattedText(txt, new CultureInfo("en-us"), System.Windows.FlowDirection.LeftToRight, tf, (double)size, System.Windows.Media.Brushes.Black, null, TextFormattingMode.Display);
       return new Size { Width = ft.WidthIncludingTrailingWhitespace, Height = ft.Height };
      }));

編集: 他の人へのリンクを見つけましたが、正確な問題ではありません。 http://www.eggheadcafe.com/software/aspnet/31783898/problem-creating-an-bitmapsource-from-an-hbitmap-in-threaded-code.aspx ~同様の問題がありますが、回答はありません

System.Windows.Media.DrawingVisual.RenderOpen() しばらくするとエラーが発生します ~ 同様の問題がありますが、回答はありません

http://connect.microsoft.com/VisualStudio/feedback/details/361469/net-3-5-sp1-breaks-use-of-wpf-under-iis# ~ 同様の例外ですが、3.5SP1 は使用していませんまたはIIS 7。

Microsoft Connect サイトからもこれを送信しました (同様の問題が発生している場合は、投票してください)。 https://connect.microsoft.com/WPF/feedback/details/654208/wpf-formattedtext-the-system-cannot-find-the-file-specified-exception-in-a-service

編集: Microsoft からの応答:「WPF オブジェクトは、スレッド プール スレッドではなく、ディスパッチャー スレッドで作成する必要があります。通常は、スレッドを専用にして、ディスパッチャー ループを実行し、オブジェクトを作成してそれらを凍結して返す要求にサービスを提供することをお勧めします。ありがとう、WPF チーム」 〜これをどのように実装しますか?

EDIT:NightDwellerのおかげで最終的な解決策

if(Application.Current == null) new Application();
(Size)Application.Current.Dispatcher.CurrentDispatcher.Invoke(new Func<Size>(() =>
        {
...});

編集: 変更 (new Application();) をデプロイすると、「同じ AppDomain に複数の System.Windows.Application インスタンスを作成できません」というエラーが記録されました。エラー ソース: PresentationFramework エラー ターゲット サイト: Void .ctor()

4

3 に答える 3

2

暗闇でのショット:

スタック トレースは、WPF が GetTextSize を実行しているスレッドで Dispatcher を見つけられないことを示しているように見えるため、ウィンドウへのハンドルの作成を含む新しいものを作成する必要があります。

これを 10 秒ごとに呼び出すと、8'640 スレッド、つまり 1 日あたりのウィンドウ数になります。Mark Russinovichによると、セッションごとに 32 K ウィンドウの制限があり、これが RegisterClassEx のエラーを説明している可能性があります。

これを克服するためのアイデアは、メイン スレッドから現在のディスパッチャーを読み取り、それをタスクに設定することです。

編集:私は別の外観を持っていましたが、スレッドのディスパッチャーを設定できないようです(自動的に作成されます)。

申し訳ありませんが、ここで何が起こっているのか理解できません。

テキスト サイズを計算するために、WPF は FormattedText インスタンスを必要とし、これは Dispatcher クラスのメンバーとして格納されます。既存の Dispatcher は弱参照のリストに格納されます。それぞれが特定のスレッドに関連付けられています。ここでは、新しい Dispatcher インスタンスが何度も作成されているように見えます。そのため、呼び出し元のスレッドが新しいか、メモリが非常に少なく、弱参照が破棄されています。最初のケース (新しいスレッド) は、タスク スケジューラがコアあたり約 25 のスレッドを持つスレッド プールを使用するため (私の記憶が正しければ)、ATOM またはウィンドウのプールを使い果たすには十分ではないため、ほとんどありません。2 番目のケースでは、HwndWrapper が IDisposable であり、Dispose メソッドが登録されたクラスを解放するため、リソースが枯渇する可能性はほとんどありません。

于 2011-03-04T16:01:24.060 に答える
1

提供した情報からすでにご存知のとおり、すべてのUI要素(FormattedTextは1つです)はUIスレッドで作成する必要があります。

探しているコードは次のとおりです。

return (Size)Application.Current.Dispatcher.CurrentDispatcher.Invoke(new Func<Size>(() =>
    {
      FormattedText ft = new FormattedText(txt, new CultureInfo("en-us"), System.Windows.FlowDirection.LeftToRight, tf, (double)size, System.Windows.Media.Brushes.Black, null, TextFormattingMode.Display);
       return new Size { Width = ft.WidthIncludingTrailingWhitespace, Height = ft.Height };
      }));

Application.Currentに注意してください。WPFアプリケーションのUIスレッドのディスパッチャーである「Application」ディスパッチャーが必要です。現在のコードは実際に現在のスレッドのディスパッチャーを作成するため、実行中のスレッドを実際に変更していません(ディスパッチャーについてはこちらを参照してください)。

于 2011-04-05T00:19:15.370 に答える
0

何か改名した?はいの場合は、そのリンクを確認してください: WPF Prism: シェルの作成に関する問題

于 2011-03-04T15:39:48.070 に答える