in our app have tracing window can enable on client locations allow debugging, accessed thought static library.
problem is, when there lot of log messages going window crashes accessviolation error. link of code crashes richtextbox.appendtext(..,..,..).
here create window.
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(); } }
and here write line textbox
public void write(string line, color color) { try { _msgqueue.enqueue(new tuple<string, color>(line, color)); methodinvoker gui = delegate { try { // getting overflow trim out 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; { 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) { // don't care exceptions in here, 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 trace"); } }
i have no idea how around one, thought calling begininvoke() needed.
looking possible, or if knows third party tool handle better happy @ that.
below modification of logger. note how _processing
, lock
used avoid reentrancy , protect _queue
. also, use synchronizationcontext
instead of control.begininvoke
avoid dependency on window disposition state. traceview
can created (with traceview.create
) , used thread, window belongs parent
window's thread , that's it's delivering text richedit
. it's possible have dedicated sta thread that, don't feel that's necessary.
[edited] i've eliminated might race condition in checking _processing
, added createonownthread
in case dedicated thread logger ui requirement. decided keep application.doevents()
cases when write
called tight loop, keep ui responsive.
usage (stress-test):
private void form1_load(object sender, eventargs ev) { var traceview = traceview.create(this); (var = 0; < 1000; i++) { var _i = i; task.run(() => { traceview.write(string.format("line: {0}\n", _i), system.drawing.color.green); }); } }
implementation:
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 protect _queue , _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 on parent window's thread parent.invoke(new action(() => { view = new traceview(parent); view.show(parent); })); return view; } private void dequeuemessages() { // make sure on ui thread debug.assert(thread.currentthread.managedthreadid == _threadid); lock (_lock) { // prevent re-entracy if (_processing) return; // mark beginning of processing _processing = true; } // process pending messages (; ; ) { tuple<string, color> msg = null; lock (_lock) { if (!_queue.any()) { // mark end of processing _processing = false; return; } msg = _queue.dequeue(); } if (this.disposing || this.isdisposed) { // not loose messages if window 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 required if logging tight loop, // keep 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 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 on 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 application.exitthread() when closed return; }); thread.setapartmentstate(apartmentstate.sta); thread.start(); sync.wait(); } return view; } } }