1

バックグラウンドスレッドからの更新に関するさらに別の質問。

要点を理解するには:アプリケーションでは、バックグラウンドスレッドがUIを更新する必要があります。中間コレクションを使用してメッセージをバッファリングし、それらを表示するタイマーを使用することを検討しました。現在、私たちは最も単純なアプローチを試みています。

コードの試み#1:

void foo(string status)
{
    if (this.InvokeRequired)
    {
        BeginInvoke(new MethodInvoker(delegate()
        {
            InsertStatusMessage(status);
        }));

    }
    else
    {
        InsertStatusMessage(status);
    }  
}

これにはいくつかの欠陥があるようです。Msdnは、ウィンドウハンドルがまだ作成されていない場合にInvokeRequiredも返されると述べています(私の意見では利用できません)。falseしたがって、コードは次のようになります。

void foo(string status)
{
    if (this.InvokeRequired)
    {
        BeginInvoke(new MethodInvoker(delegate()
        {
            InsertStatusMessage(status);
        }));

        // wait until status is set
        EndInvoke(result);
    }
    else if(this.IsHandleCreated)
    {
        InsertStatusMessage(status);
    }
    else
    {
        _logger.Error("Could not update status");
    } 
}

上記のコードもどういうわけかスローします(不明で複製されていない理由のため)。DevExpressを使用しており、これは未処理の例外メッセージです(エラーが発生した場所/場所に関する情報も手がかりもありません)。

System.NullReferenceException:DevExpressのDevExpress.Utils.Text.TextUtils.GetFontAscentHeight(Graphics g、Font font)のDevExpress.Utils.Text.FontsCache.GetFontCacheByFont(Graphics graphics、Font font)のオブジェクトのインスタンスにオブジェクト参照が設定されていません.XtraEditors.ViewInfo.BaseEditViewInfo.GetTextAscentHeight()in DevExpress.XtraEditors.ViewInfo.TextEditViewInfo.CalcTextBaseline(Graphics g)in DevExpress.XtraEditors.ViewInfo.BaseControlViewInfo.ReCalcViewInfo(Graphics g、MouseButtons buttons、Point mousePosition、Rectangle bounds)in DevExpress XtraGrid.Views.Grid.ViewInfo.GridViewInfo.UpdateCellEditViewInfo(GridCellInfo cell、Point mousePos、Boolean canFastRecalculate、Boolean calc)in DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo.CreateCellEditViewInfo(GridCellInfo cell、Boolean calc、DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo.RequestCellEditViewInfo(GridCellInfoセル)のDevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawRegularRowCell(GridViewDrawArgs e、GridCellInfo ci)のブールallowCache) .Drawing.GridPainter.DrawRegularRow(GridViewDrawArgs e、GridDataRowInfo ri)in DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawRow(GridViewDrawArgs e、GridRowInfo ri)in DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawRows(GridViewDrawArgs e)DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.DrawContents(GridViewDrawArgs e)in DevExpress.XtraGrid.Views.Grid.Drawing.GridPainter.Draw(ViewDrawArgs ee)in DevExpress.XtraGrid.Views.Base.BaseView.Draw (GraphicsCache e)DevExpress.XtraGrid.GridControlにあります。OnPaint(PaintEventArgs e)
System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e、Int16 layer、Boolean disposeEventArgs)in System.Windows.Forms.Control.WmPaint(Message&m)in System.Windows.Forms.Control.WndProc(Message&m)in DevExpress .XtraEditors.Container.EditorContainer.WndProc(Message&m)
in DevExpress.XtraGrid.GridControl.WndProc(Message&m)in System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message&m)
in System.Windows.Forms.NativeWindow.Callback (IntPtr hWnd、Int32 msg、IntPtr wparam、IntPtr lparam)

必要なもの(メソッドデリゲート)が少なくて読みやすいので、Begin/End Invoke代わりに使用したいと思います。Invoke

何を見逃しましたか?どうすれば安全にスレッド呼び出しを行うことができますか?リストボックスにメッセージを追加したいだけです。呼び出し元のスレッドが数ミリ秒待機するかどうかは本当に気にしません。

4

3 に答える 3

2

「MethodInvoker」を使用して「Invoke」を直接呼び出すことができます。

void foo(string status)
{
    Invoke(new MethodInvoker(() => {InsertStatusMessage(status);}));
}

これをDevExpressコントロールでも使用しました(特に、1つのフォーム上の複数のXtragridのデータソースを非同期更新するため)。

MethodInvokerの詳細については、優れた投稿があります。

于 2012-04-12T09:53:25.970 に答える
1
class Test : Form
{
        delegate void FooCallback(string status);


        public Test()
        {
        }

        private void foo(string status)
        {
            if (this.InvokeRequired == true)
            {
                FooCallback = new FooCallback(foo);
                this.Invoke
                    (d, status);

            }
            else
            {
              //Do Things

            }
        }     
}

MethodInvokerを使用すると、パフォーマンスが大幅に低下します

于 2013-09-02T09:32:14.313 に答える
0

適用されたロジックをリバースエンジニアリングしましたが、すべての場合で、エラーの原因となる1つの共通の要因がありました。

  1. オブジェクト参照がオブジェクト インスタンスに設定されていません。
  2. オブジェクトは現在他の場所で使用されています。

エラーはの導入です

DevExpress.Utils.Text.FontsCache.GetFontCacheByFont(Graphics graphics, Font font)

このクラスはスレッド間でWindowsフォントを共有し、それは厳しく禁止されています。ペイントメッセージ処理中のGDIリソースのWindows使用だけでなく、

DevExpress.Utils.Text.TextUtils.GetStringSize(Graphics g, String text, 
    Font font, StringFormat stringFormat, Int32 maxWidth, Int32 maxHeight, 
    IWordBreakProvider wordBreakProvider, Boolean& isCropped)

GDIリソースを作成したスレッドに制限されます。言い換えると:

フォントまたはウィンドウを作成したスレッドのみがそれを使用できます。他のすべてのスレッドは、それらのウィンドウにウィンドウペイントメッセージを送信することはできません。

ウィンドウ内のフォントのサイズを測定することも、ある種のペイントアクションです(ただし、ペイントされたテキストのサイズのみが返されるため、表示されません)。

解決策(両方の一方のみをDevExpressで実装する必要があります):

  1. クラスText.FontsCacheは、作成されたフォントをキャッシュに格納するために使用されるIDにcurrent-thread-IDを追加する必要があります。
  2. 各スレッドは、クラスの独自のインスタンスを作成する必要がありますText.FontsCache
于 2015-11-12T08:46:50.103 に答える