【问题标题】:Multithreading and form invoke are not in sync多线程和表单调用不同步
【发布时间】: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


【解决方案1】:

你不能。当您中止另一个线程时,该线程已经向消息队列发送了一条消息,说明应该添加一个项目。之后线程发生的事情不会停止消息。

通常使用Thread.Abort 也是一个非常糟糕的主意。在任何时间点抛出异常的想法真的很难处理。在这种情况下,很多代码块真的很难推理。我强烈建议您使用CancellationToken 之类的东西来执行取消操作。这可确保取消只能在您知道停止执行安全的特定时间点发生。

【讨论】:

  • 出于谷歌搜索的目的,您详细描述的模式通常称为Cooperative cancellation
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多