【发布时间】:2014-01-08 16:17:43
【问题描述】:
我正在使用具有多线程和表单方法调用功能的 C# 开发 Windows 应用程序。这是屏幕和代码
如您所见,我的表单有两个按钮和一个列表视图。单击每个按钮时,一组 100 个项目将添加到列表视图中。这些是使用单独的线程添加的。这是表单代码
private void btnTest1_Click(object sender, EventArgs e)
{
LogWriter.Log("Test1 click started");
if (this.t != null && this.t.IsAlive)
{
this.t.Abort();
LogWriter.Log("Thread aborted in test1 click");
}
this.testComponent.InitialValue = 1;
this.t = new Thread(this.ts);
LogWriter.Log("New thread started in test1 click");
this.t.Start();
}
private void btnTest2_Click(object sender, EventArgs e)
{
LogWriter.Log("Test2 click started");
if (this.t != null && this.t.IsAlive)
{
LogWriter.Log("Thread aborted in test2 click");
this.t.Abort();
}
this.testComponent.InitialValue = 4000;
this.t = new Thread(this.ts);
LogWriter.Log("New thread started in test2 click");
this.t.Start();
}
internal void AddToList(ListViewItem listItem)
{
this.Invoke((MethodInvoker)delegate
{
this.listView1.Items.Add(listItem);
LogWriter.Log("Added " + listItem.Text);
});
}
private void Form1_Load(object sender, EventArgs e)
{
LogWriter.ClearLog();
this.testComponent = new TestComponent();
this.ts = new ThreadStart(this.testComponent.DoSomething);
this.t = new Thread(ts);
}
这里是 TestComponent 类
internal class TestComponent
{
public int InitialValue { get; set; }
public void DoSomething()
{
for (int i = this.InitialValue; i <= InitialValue + 100; i++)
{
ListViewItem listItem = new ListViewItem();
listItem.Text = i.ToString();
Program.AppForm1.AddToList(listItem);
}
}
}
LogWriter 只是将事件记录到文本文件中。 这里的概念是,只要用户单击 Test1 按钮,就会将一组项目添加到列表中。即使正在添加项目,用户也可以点击 Test2 按钮,这将停止当前添加项目并开始添加新的项目集。
当我从一个单独的线程向列表视图添加项目时,我正在使用 Invoke 方法。因此,当我一个接一个地单击 Test1 和 Test2 按钮时,我在日志中得到以下结果。
2014-01-08 21:29:12.764 - Test1 click started
2014-01-08 21:29:12.778 - New thread started in test1 click
2014-01-08 21:29:12.788 - Added 1
2014-01-08 21:29:12.791 - Added 2
2014-01-08 21:29:12.796 - Added 3
2014-01-08 21:29:12.802 - Added 4
2014-01-08 21:29:13.127 - Test2 click started
2014-01-08 21:29:13.129 - Thread aborted in test2 click
2014-01-08 21:29:13.149 - New thread started in test2 click
2014-01-08 21:29:13.154 - Added 5
2014-01-08 21:29:13.157 - Added 4000
2014-01-08 21:29:13.160 - Added 4001
2014-01-08 21:29:13.163 - Added 4002
2014-01-08 21:29:13.166 - Added 4003
从上面的日志中可以看出,不应记录“Added 5”,因为它上面的条目说“线程已中止并启动了一个新线程”。现在我的问题是,
当调用者线程被中止时如何停止调用的执行或
如何等到invoke方法执行完毕后再启动一个新线程。
【问题讨论】:
-
这是一个标准的线程错误,称为“竞争条件”。你很幸运能及早发现它,如果你使用 BeginInvoke() 来代替它,就不太可能看到它。修复它没有多大意义,添加的项目数量无论如何都是完全随机的。
-
中止线程会在线程内异步调用 ThreadAbortException。这意味着线程中的工作不会停止,直到 ThreadAbortException 被捕获(或者如果没有被捕获则抛出)。这留下了一些时间(可能不是很长,但足够长)让线程明显地做一些工作。在线程之间进行通信时,线程之间总会存在延迟。您可以这样做,以便在线程实际中止之前不输出“中止”(因此您在“中止”之前看到 5),但这会为相同的结果增加额外的开销。
标签: c# multithreading invoke