18

プロジェクトで、ソースがないライブラリからのビジュアルコントロールを使用しています。
これらの3つのコントロールを一度に画面に表示してUIの応答性を高めるには、更新に時間がかかりすぎます(およそ200ミリ秒)。(3つすべてを一度に更新する必要があるかもしれません。これにより、すべてが考えている間、UIが約600ミリ秒間スタックしたままになります)。

TaskSchedulersに関するいくつかの投稿を読み、これらの各コントロールを独自のスレッドで実行する方法として、並列タスク機能の調査を開始しています。プラットフォームはマルチコアになるので、同時処理を活用したいと思います。

問題は、私がこれについてどうやって行くのかわからないことさえ知らないということです。

WPFのメインUIスレッドとは別のスレッドでコントロールを実行するための適切なデザインパターンはありますか?

具体的には、サードパーティのマップコントロールであり、新しい場所またはズームレベルを指定すると、再描画に非常に長い時間がかかります(〜200ms)。おそらくこれらのうち3つを最大4Hzで更新すると、明らかに追いつかなくなります
。WPFコントロールをユーザーコントロールにカプセル化したので、ユーザー入力(マウスクリック)をキャプチャしながら、各インスタンスを独自のスレッドで実行する必要があります。 、 例えば)。

更新:私は解決策を探していますが、これまでに次のことを実装しました。
私のメイン(UI)スレッドは、問題のコントロールを含む新しいウィンドウを作成するスレッドを生成し、それを正しい位置に配置します(これにより、通常のコントロールのように見えます)。

_leftTopThread = new Thread(() =>
{
   _topLeftMap = new MapWindow()
   {
      WindowStartupLocation = WindowStartupLocation.Manual,
      Width = leftLocation.Width,
      Height = leftLocation.Height,
      Left = leftLocation.X,
      Top = leftLocation.Y,
      CommandQueue = _leftMapCommandQueue,
   };

   _topLeftMap.Show();
   System.Windows.Threading.Dispatcher.Run();

});

_leftTopThread.SetApartmentState(ApartmentState.STA);
_leftTopThread.IsBackground = true;
_leftTopThread.Name = "LeftTop";
_leftTopThread.Start();

マップにコマンドを送信する(場所を移動するなど)ためのスレッドセーフなBlockingCollectionCommandQueueキューはどこにありますか。 問題は、私がどちらかをできるようになったということです

  • System.Windows.Threading.Dispatcher.Run()呼び出しによるユーザー入力があります
  • またはCommandQueueでブロックし、メインスレッドから送信されたコマンドをリッスンします

コマンドを待ってスピンすることはできません。スレッドのCPUをすべて吸収してしまうからです。イベントメッセージポンプ
をブロックして機能させることは可能ですか?

4

2 に答える 2

13

まあ、私はうまくいく方法を持っています-しかしそれはおそらく最もエレガントではないかもしれません。

XAMLにサードパーティ(低速レンダリング)コントロールを含むウィンドウがあります。

public partial class MapWindow : Window
{
    private ConcurrentQueue<MapCommand> _mapCommandQueue;
    private HwndSource _source;

    // ...

}

私のメイン(UI)スレッドは、スレッド上でこのウィンドウを構築して開始します。

_leftTopThread = new Thread(() =>
{
   _topLeftMap = new MapWindow()
   {
      WindowStartupLocation = WindowStartupLocation.Manual,
      CommandQueue = _leftMapCommendQueue,
   };

    _topLeftMap.Show();
    System.Windows.Threading.Dispatcher.Run();

});

_leftTopThread.SetApartmentState(ApartmentState.STA);
_leftTopThread.IsBackground = true;
_leftTopThread.Name = "LeftTop";
_leftTopThread.Start();

次に、スレッド内のウィンドウへのハンドルを取得します(初期化後)。

private IntPtr LeftHandMapWindowHandle
{
    get
    {
        if (_leftHandMapWindowHandle == IntPtr.Zero)
        {
            if (!_topLeftMap.Dispatcher.CheckAccess())
            {
                _leftHandMapWindowHandle = (IntPtr)_topLeftMap.Dispatcher.Invoke(
                  new Func<IntPtr>(() => new WindowInteropHelper(_topLeftMap).Handle)
                );
            }
            else
            {
                _leftHandMapWindowHandle = new WindowInteropHelper(_topLeftMap).Handle;
            }
        }
        return _leftHandMapWindowHandle;
    }
}

..そして、スレッド化されたウィンドウと共有されるスレッドセーフキューにコマンドを配置した後:

var command = new MapCommand(MapCommand.CommandType.AircraftLocation, new object[] {RandomLatLon});
_leftMapCommendQueue.Enqueue(command);

..キューをチェックできることを知らせました:

PostMessage(LeftHandMapWindowHandle, MapWindow.WmCustomCheckForCommandsInQueue, IntPtr.Zero, IntPtr.Zero);

ウィンドウはウィンドウメッセージにフックされているため、ウィンドウは私のメッセージを受信できます。

protected override void OnSourceInitialized(EventArgs e)
{
    base.OnSourceInitialized(e);

    _source = PresentationSource.FromVisual(this) as HwndSource;
    if (_source != null) _source.AddHook(WndProc);
}

..それからそれはチェックすることができます:

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) // 
{
    // Handle messages...
    var result = IntPtr.Zero;

    switch (msg)
    {
        case WmCustomCheckForCommandsInQueue:
            CheckForNewTasks();
            break;

    }
    return result;
}

..そしてスレッドで実行します!

private void CheckForNewTasks()
{
    MapCommand newCommand;
    while (_mapCommandQueue.TryDequeue(out newCommand))
    {
        switch (newCommand.Type)
        {
            case MapCommand.CommandType.AircraftLocation:
                SetAircraftLocation((LatLon)newCommand.Arguments[0]);
                break;

            default:
                Console.WriteLine(String.Format("Unknown command '0x{0}'for window", newCommand.Type));
                break;
        }
    }
}

そのように簡単..:)

于 2012-04-12T10:49:47.277 に答える
6

私もこれを調べており、私が見つけた最も関連性のある情報は、このブログ投稿にありました(ただし、まだテストしていません)。

http://blogs.msdn.com/b/dwayneneed/archive/2007/04/26/multithreaded-ui-hostvisual.aspx

UIスレッドでHostVisualを作成し、次にバックグラウンドスレッドを起動し、MediaElementを作成し、それをVisualTarget(HostVisualを指す)内に配置し、すべてをハッキーなVisualTargetPresentationSource内に配置します。

この方法の問題は、明らかにユーザーが新しいスレッドで実行されているコントロールを操作できないことです。

于 2012-04-05T13:24:54.403 に答える