0

私たちのアプリには、デバッグを許可するためにクライアントの場所で有効にできるトレース ウィンドウがあり、静的ライブラリを介してアクセスされます。

問題は、ウィンドウに大量のログ メッセージが送信されると、AccessViolation エラーでクラッシュすることです。クラッシュするコードのリンクは、RichTextBox.AppendText(..,..,..) です。

ここでウィンドウを作成します。

public static void Start(Form parent)
{
  if (_runningThread == null || !_runningThread.IsAlive)
  {

      _runningThread = new Thread(() =>
          {
              _traceView = new TraceView(parent) { Text = "Tracing ~ " + parent.Text };
              Application.Run(_traceView);

          });

      _runningThread.SetApartmentState(ApartmentState.MTA);
      _runningThread.Start();
  }

}

これは、テキストボックスに行を書き込んだ場合です

public void Write(string line, Color color)
{
  try
  {
      _msgQueue.Enqueue(new Tuple<string, Color>(line, color));

      MethodInvoker gui = delegate
          {
              try
              {
                  // Was getting an overflow so trim out some lines
                  if (uiTrace.Lines.Length > 5000)
                  {
                      uiTrace.Lines = new string[0];
                      uiTrace.SelectionStart = uiTrace.TextLength;
                      Application.DoEvents();
                  }

                  while (_msgQueue.Count != 0)
                  {

                      bool retry;
                      var count = 0;
                      do
                      {
                          try
                          {
                              count++;
                              if (_indent < 0)
                                  _indent = 0;

                              var msg = _msgQueue.Dequeue();
                              var selectionStart = uiTrace.TextLength;
                              uiTrace.AppendText(string.Format("[{0}] {1}{2}", _stopwatch.ElapsedMilliseconds, string.Empty.PadLeft(_indent * 4), msg.Item1));


                              uiTrace.Select(selectionStart, uiTrace.TextLength);
                              uiTrace.SelectionColor = msg.Item2;
                              uiTrace.SelectionStart = uiTrace.TextLength;
                              uiTrace.ScrollToCaret();
                              retry = false;
                          }
                          catch (Exception)
                          {
                              retry = true;
                          }
                      } while (retry && count < 5);
                  }
              }
              catch (Exception)
              {
                  // We don't care about exceptions in here, for now anyway
              }
          };

      if (uiTrace.InvokeRequired && !uiTrace.Disposing && !uiTrace.IsDisposed)
      {
          uiTrace.BeginInvoke(gui);
          return;
      }
      gui();
  }
  catch (Exception)
  {

   //   QIT_Backoffice.Processes.Errors.ErrorHandler.WriteErrorLog(Sources.SourceEnum.External, ex, "Error writing to trace");
  }
}

これを回避する方法が本当にわかりません.BeginInvoke()を呼び出すことが必要だと思いました。

可能なヘルプを探しているか、これをより適切に処理できるサードパーティのツールを誰かが知っている場合は、喜んでそれを見てください.

4

2 に答える 2

0

以下は、あなたのロガーの私の修正です。_processinglockが再入可能性を回避し、 を保護するためにどのように使用されるかに注意してください_queue。また、ウィンドウの配置状態への依存を避けるためSynchronizationContextに代わりに使用します。は任意のスレッドから( を使用して) 作成および使用できますが、そのウィンドウはウィンドウのスレッドに属し、テキストを に配信する場所でもあります。そのための専用の STA スレッドを持つことは可能ですが、私はそれが必要だとは思いません。Control.BeginInvokeTraceViewTraceView.Createparentrichedit

[編集]ロガー UI の専用スレッドが必要な場合に備えて、チェック時に競合状態になる可能性のあるものを排除し_processing、追加しました。UI の応答性を維持するために、 がタイト ループから呼び出された場合CreateOnOwnThreadにも対応することにしました。Application.DoEvents()Write

使用法 (ストレステスト):

private void Form1_Load(object sender, EventArgs ev)
{
    var traceView = TraceView.Create(this);
    for (var i = 0; i < 1000; i++)
    {
        var _i = i;
        Task.Run(() => 
        {
            traceView.Write(String.Format("Line: {0}\n", _i), System.Drawing.Color.Green);
        });
    }
}

実装:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Logger
{
    public partial class TraceView : Form
    {
        private Form _parent = null;

        private SynchronizationContext _context = SynchronizationContext.Current;
        private int _threadId = Thread.CurrentThread.ManagedThreadId;

        private object _lock = new Object(); // sync lock to protect _queue and _processing
        private Queue<Tuple<string, Color>> _queue = new Queue<Tuple<string, Color>>();
        private volatile bool _processing = false; // reentracy check flag

        public TraceView(Form parent)
        {
            _parent = parent;
            InitializeComponent();
        }

        public static TraceView Create(Form parent)
        {
            TraceView view = null;
            // create it on the parent window's thread
            parent.Invoke(new Action(() => {
                view = new TraceView(parent);
                view.Show(parent);
            }));
            return view;
        }

        private void DequeueMessages()
        {
            // make sure we are on the UI thread
            Debug.Assert(Thread.CurrentThread.ManagedThreadId == _threadId); 

            lock (_lock)
            {
                // prevent re-entracy
                if (_processing)
                    return;
                // mark the beginning of processing
                _processing = true;
            }

            // process pending messages
            for (; ; )
            {
                Tuple<string, Color> msg = null;

                lock (_lock)
                {
                    if (!_queue.Any())
                    {
                        // mark the end of processing
                        _processing = false;
                        return;
                    }
                    msg = _queue.Dequeue();
                }

                if (this.Disposing || this.IsDisposed)
                {
                    // do not just loose messages if the window is disposed
                    Trace.Write(msg.Item1); 
                }
                else
                {
                    var selectionStart = _richTextBox.TextLength;
                    _richTextBox.AppendText(msg.Item1);
                    _richTextBox.Select(selectionStart, _richTextBox.TextLength);
                    _richTextBox.SelectionColor = msg.Item2;
                    _richTextBox.SelectionStart = _richTextBox.TextLength;
                    _richTextBox.ScrollToCaret();
                    _richTextBox.Refresh(); // redraw;
                    // DoEvents is required if logging from a tight loop, 
                    // to keep the UI responsive
                    Application.DoEvents(); 
                }
            }
        }

        public void Write(string line, Color color)
        {
            lock (_lock)
            {
                _queue.Enqueue(new Tuple<string, Color>(line, color));
                // prevent re-entracy
                if (_processing)
                    return; // DequeueMessages is already in progress
            }

            if (Thread.CurrentThread.ManagedThreadId == _threadId)
                DequeueMessages();
            else
                _context.Post((_) => 
                { 
                    DequeueMessages();  
                }, null);
        }

        public static TraceView CreateOnOwnThread()
        {
            TraceView view = null;
            using (var sync = new ManualResetEventSlim())
            {
                // create it on its own thread
                var thread = new Thread(() =>
                {
                    SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
                    view = new TraceView(null);
                    view.Show();
                    sync.Set(); // ready Write calls
                    Application.Run(view); // view does Application.ExitThread() when closed
                    return;
                });

                thread.SetApartmentState(ApartmentState.STA);
                thread.Start();
                sync.Wait();
            }
            return view;
        }

    }
}
于 2013-08-28T05:37:41.907 に答える
0

C# よりも VB での .Net 経験が豊富ですが、次のコードは使用しません。

  if (uiTrace.InvokeRequired && !uiTrace.Disposing && !uiTrace.IsDisposed)
  {
      uiTrace.BeginInvoke(gui);
      return;
  }
  gui();

ステートメント内の if などで呼び出されるだけでなくgui、.InvokeRequiredIfgui()

しません:

  If (uiTrace.InvokeRequired && !uiTrace.Disposing && !uiTrace.IsDisposed)
  {
      uiTrace.BeginInvoke(gui);
      return;
  }
  Else
      gui();

より適切ですか?

于 2013-08-28T02:24:51.460 に答える