8

非 UI スレッドから UI コントロールを更新すると、デリゲートと.InvokeRequired.

例えば:

public delegate void DelegateUIUpdate();
private void UIUpdate()
{
    if (someControl.InvokeRequired)
    {
        someControl.Invoke(new DelegateUIUpdate(UIUpdate));
        return;
    }
    // do something with someControl
}

これがループまたはタイマー間隔で呼び出されると、プログラムのハンドルが一貫して増加します。

編集:

上記がコメントアウトされ、そのように修正された場合:

public delegate void DelegateUIUpdate();
private void UIUpdate()
{
    //if (someControl.InvokeRequired)
    //{
    //   someControl.Invoke(new DelegateUIUpdate(UIUpdate));
    //    return;
    //}
    CheckForIllegalCrossThreadCalls = false;
    // do something with someControl
}

...その後、ハンドルはインクリメントを停止しますが、もちろんクロススレッド呼び出しを許可したくありません。

編集2:

ハンドルの増加を示すサンプルを次に示します。

Thread thread;
private delegate void UpdateGUI();
bool UpdateTheGui = false;

public Form1()
{
    InitializeComponent();

    thread = new Thread(new ThreadStart(MyThreadLoop));
    thread.Start();
}

private void MyThreadLoop()
{
    while (true)
    {
        Thread.Sleep(500);
        if (UpdateTheGui)
        {
            UpdateTheGui = false;
            UpdateTheGuiNow();
        }
    }
}

private void UpdateTheGuiNow()
{
    if (label1.InvokeRequired)
    {
        label1.Invoke(new UpdateGUI(UpdateTheGuiNow));
        return;
    }

    label1.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss");
    label2.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss");
    label3.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss");
}

private void btnInvoke_Click(object sender, EventArgs e)
{
    UpdateTheGui = true;
}
4

8 に答える 8

4

私は同じ問題を抱えていました

this.Invoke(new DelegateClockUpdate(ChangeClock), sender, e);

呼び出しごとに1つのハンドルを作成します。

Invokeは同期であり、事実上ハンドルがぶら下がっているため、ハンドルが増加します。

結果を処理するには、待機ハンドルを使用するか、以下に示すように非同期BeginInvokeメソッドを使用する必要があります。

this.BeginInvoke(new DelegateClockUpdate(ChangeClock), sender, e);    
于 2011-10-22T11:33:28.983 に答える
3

Control.Invoke() メソッドはハンドルを消費しません。ただし、このコードは明らかにスレッドから呼び出されます。スレッドハンドルを 5 つ消費します。

Thread クラスには Dispose() メソッドがありませんが、あるべきです。これはおそらく設計によるものであり、確実に呼び出すことは非常に困難であり、スレッドプール スレッドでは不可能なことです。スレッドが必要とする 5 つのハンドルは、ファイナライザーによって解放されます。ファイナライザが実行されない場合、プログラムで必要となるハンドルの量は増え続けます。

ファイナライザーが実行されないことは非常に珍しいことです。多くのスレッドを開始するが、多くのメモリを割り当てないプログラムが必要になります。これは、静的テストでのみ発生する傾向があります。Perfmon.exe を使用してこの状態を診断し、.NET メモリ パフォーマンス カウンターを使用して、gen #0 コレクションが実行されているかどうかを確認できます。

これが本番プログラムで発生した場合は、ランナウェイ ハンドル リークを回避するために GC.Collect() を自分で呼び出す必要があります。

于 2010-06-15T20:10:49.190 に答える
3

私は自分のコードで同じことを見てきました。に置き換えることで解決しましInvokeBeginInvoke。ハンドル漏れがなくなりました。

ドロン。

于 2011-08-03T12:29:05.690 に答える
1

実際、JYelton と同じ問題が発生しています。UI を更新するために、スレッド内から同じ呼び出しを行います。

回線が呼び出されるとすぐにsomeControl.Invoke(new DelegateUIUpdate(UIUpdate));、ハンドルが 1 つ増えます。起動時に何らかのリークがあることは確かですが、何が原因なのかわかりません。これは、いくつかのシステムで確認されています。

于 2010-06-15T20:16:14.447 に答える
1

明示的なハンドル ファイナライズを使用した非同期呼び出し。例:

  public static class ActionExtensions
  {
    private static readonly ILog log = LogManager.GetLogger(typeof(ActionExtensions));

    /// <summary>
    /// Async exec action.
    /// </summary>
    /// <param name="action">Action.</param>
    public static void AsyncInvokeHandlers(
      this Action action)
    {
      if (action == null)
      {
        return;
      }

      foreach (Action handler in action.GetInvocationList())
      {
        // Initiate the asychronous call.  Include an AsyncCallback
        // delegate representing the callback method, and the data
        // needed to call EndInvoke.
        handler.BeginInvoke(
          ar =>
          {
            try
            {
              // Retrieve the delegate.
              var handlerToFinalize = (Action)ar.AsyncState;
              // Call EndInvoke to free resources.
              handlerToFinalize.EndInvoke(ar);

              var handle = ar.AsyncWaitHandle;
              if (handle.SafeWaitHandle != null && !handle.SafeWaitHandle.IsInvalid && !handle.SafeWaitHandle.IsClosed)
              {
                ((IDisposable)handle).Dispose();
              }
            }
            catch (Exception exception)
            {
              log.Error("Async Action exec error.", exception);
            }
          },
          handler);
      }
    }
  }

http://msdn.microsoft.com/en-us/library/system.iasyncresult.asyncwaithandle.aspxメモを参照してください。

デリゲートの BeginInvoke メソッドを使用してメソッドを非同期的に呼び出し、結果の IAsyncResult から待機ハンドルを取得する場合は、待機ハンドルの使用が終了したらすぐに、WaitHandle.Close メソッドを呼び出して待機ハンドルを閉じることをお勧めします。待機ハンドルへのすべての参照を単純に解放すると、ガベージ コレクションが待機ハンドルを再利用するときにシステム リソースが解放されますが、破棄可能なオブジェクトを明示的に閉じるか破棄すると、ガベージ コレクションがより効率的に機能します。詳細については、AsyncResult.AsyncWaitHandle プロパティを参照してください。

于 2014-04-03T12:44:43.410 に答える
0

Invokeこれは、UI スレッドへの更新をマーシャリングするために使用する標準パターンです。

あなたの問題が、あなたの質問に含まれていないアプリケーションの他のコードによって引き起こされていないことを確信していますか?

于 2010-06-15T19:45:05.227 に答える
0

関係ないと思います。おそらく、ガベージ コレクターが Invoke() 内の新しく割り当てられたオブジェクトを破棄するのを待っているだけです。

于 2010-06-15T19:45:28.577 に答える