【发布时间】:2015-10-04 07:43:09
【问题描述】:
我试图在主线程中调用一个方法来更新多个 UI 元素。这些元素之一是RichTextView。我找到了 3 种更新 UI 的方法,其中所有 3 种在运行一段时间后都会崩溃并出现以下错误。一旦我将 RichTextView 更改为简单文本框,类型 2 就不会再崩溃(我仍然不确定是否是这种情况)。
“System.StackOverflowException”类型的未处理异常 发生在 System.Windows.Forms.dll 中
我的简化代码
// Type 1
private readonly SynchronizationContext synchronizationContext;
public Form1() {
InitializeComponent();
// Type 1
synchronizationContext = SynchronizationContext.Current;
}
//Type 3
public void Log1(object message) {
Invoke(new Log1(Log), message);
}
public void Log(object message) {
if (this.IsDisposed || edtLog.IsDisposed)
return;
edtLog.AppendText(message.ToString() + "\n");
edtLog.ScrollToCaret();
Application.DoEvents();
}
private void btnStart_Click(object sender, EventArgs e) {
for (int i = 0; i < 10000; i++) {
ThreadPool.QueueUserWorkItem(Work, i);
}
Log("Done Adding");
}
private void Work(object ItemID) {
int s = new Random().Next(10, 15);// Will generate same random number in different thread occasionally
string message = ItemID + "\t" + Thread.CurrentThread.ManagedThreadId + ",\tRND " + s;
//Type1
synchronizationContext.Post(Log, message);
// Type 2
//Invoke(new Log1(Log), message);
// Type 3
//Log1(message);
Thread.Sleep(s);
}
完整代码
using System;
using System.Threading;
using System.Windows.Forms;
namespace Test {
public delegate void Log1(string a);
public partial class Form1 : Form {
private System.ComponentModel.IContainer components = null;
private Button btnStart;
private TextBox edtLog;
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent() {
this.btnStart = new Button();
this.edtLog = new TextBox();
this.SuspendLayout();
this.btnStart.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
this.btnStart.Location = new System.Drawing.Point(788, 12);
this.btnStart.Size = new System.Drawing.Size(75, 23);
this.btnStart.Text = "Start";
this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
this.edtLog.Anchor = (AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right);
this.edtLog.Location = new System.Drawing.Point(12, 41);
this.edtLog.Multiline = true;
this.edtLog.ScrollBars = ScrollBars.Vertical;
this.edtLog.Size = new System.Drawing.Size(851, 441);
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.ClientSize = new System.Drawing.Size(875, 494);
this.Controls.Add(this.edtLog);
this.Controls.Add(this.btnStart);
this.ResumeLayout(false);
this.PerformLayout();
}
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
// Type 1
private readonly SynchronizationContext synchronizationContext;
public Form1() {
InitializeComponent();
// Type 1
synchronizationContext = SynchronizationContext.Current;
}
//Type 3
public void Log1(object message) {
Invoke(new Log1(Log), message);
}
public void Log(object message) {
if (this.IsDisposed || edtLog.IsDisposed)
return;
edtLog.AppendText(message.ToString() + "\n");
edtLog.ScrollToCaret();
Application.DoEvents();
}
private void btnStart_Click(object sender, EventArgs e) {
for (int i = 0; i < 10000; i++) {
ThreadPool.QueueUserWorkItem(Work, i);
}
Log("Done Adding");
}
private void Work(object ItemID) {
int s = new Random().Next(10, 15);// Will generate same random number in different thread occasionally
string message = ItemID + "\t" + Thread.CurrentThread.ManagedThreadId + ",\tRND " + s;
//Type1
synchronizationContext.Post(Log, message);
// Type 2
//Invoke(new Log1(Log), message);
// Type 3
//Log1(message);
Thread.Sleep(s);
}
}
}
问题 1
为什么以及何时应该使用SynchronizationContext 或Invoke。有什么区别(如果我错了,请纠正我,因为我在 winform 上运行,SynchronizationContext.Current 始终存在)?
问题 2 为什么我在这里收到 StackOverflow 错误?难道我做错了什么?在单独的方法或同一个worker方法中调用invoke有什么区别(Log1崩溃而直接调用Invoke没有)
问题 3
当用户在线程完成其工作之前关闭应用程序时,我得到一个异常说 Form1 已被释放并且在调用 Log 方法(任何 3 种类型)时不可访问。我应该在线程(包括主线程)中处理异常吗?
【问题讨论】:
标签: .net multithreading visual-studio-2010 c#-4.0