【问题标题】:Terminating/joining a thread in C# [duplicate]在 C# 中终止/加入线程 [重复]
【发布时间】:2016-10-05 03:43:17
【问题描述】:

我似乎无法在 C# 中终止我的线程。该程序似乎陷入了 FormClosing 事件的无限循环中。

EDIT // 当 FormClosing 事件被触发时,我试图结束线程并关闭整个程序。

代码如下:

public partial class Form1 : Form
{
    private Thread thread;
    private volatile bool threadRunning = true;

    public Form1()
    {
        InitializeComponent();
    }

    private void Loop()
    {
        Console.WriteLine(threadRunning);
        while (threadRunning)
        {
            MethodInvoker mi = delegate { timeLabel.Text = TimeWriterSingleton.Instance.OutputTime(); };
            Invoke(mi);
        }
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        thread = new Thread(Loop);
        thread.Start();
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        threadRunning = false;
        thread.Join();
    }
}

【问题讨论】:

标签: c# multithreading


【解决方案1】:

您的Join 阻塞了GUI 线程,而您在另一个线程中的Invoke 正在等待您的GUI 线程处理委托。

快速解决方法是使用BeginInvoke 而不是Invoke,从而发布而不是发送窗口消息。

或者,不要加入。这段代码的目的是为了自己清理,你为什么关心线程何时死掉?

第三个解决方法是通过Thread.AbortEnvironment.Exit 直接删除线程。它可能会跳过一些清理工作,但您的特定代码不应该关心,关键是无论如何都要退出。

编辑:使用BeginInvoke 的工作代码如下:

    private void Loop()
    {
        while (threadRunning)
        {
            BeginInvoke(new MethodInvoker(() => timeLabel.Text = DateTime.Now.ToString()));
            Thread.Sleep(100);
        }
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        threadRunning = false;
        thread.Join();
    }

原始代码的问题在于它的运行速度与您的 CPU 允许的速度一样快,将消息队列填充到 GUI 线程无法跟上的程度。与简单地将数字添加到队列相比,更新 Windows 控件非常昂贵。所以我在 UI 更新之间添加了一个暂停来让 GUI 线程呼吸。

对于反对者,我很好奇你为什么要这么做。我所说的一切都不是事实。

【讨论】:

  • Begininvoke 不允许表单关闭。它永远在运行
  • 其他替代方案可能有效。但我正在尝试解决他的具体情况。这似乎是竞争条件的一个例子。我之前的答案(我删除了)第一次工作,但从那里开始就没有了。我删除了它。
  • 这不是竞争条件,BeginInvoke 工作得很好。我建议在进一步的 cmets 之前复习 .NET 多线程。
  • 好的。会那样做。谢谢。
【解决方案2】:

我决定改用计时器。代码现在看起来像这样,并且应用程序可以运行:

public partial class Form1 : Form
{
    private System.Timers.Timer timer;

    public Form1()
    {
        InitializeComponent();

        timer = new System.Timers.Timer(60000);
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        timeLabel.Text = TimeWriterSingleton.Instance.OutputTime();
        timer.Elapsed += TimerElapsed;
        timer.Enabled = true;
    }

    private void TimerElapsed(object sender, ElapsedEventArgs e)
    {
        timeLabel.Text = TimeWriterSingleton.Instance.OutputTime();
    }
}

【讨论】:

  • 需要注意的是定时器构造函数(60000)中的值是以毫秒为单位的。所以这将每分钟运行一次。而且它几乎不会与系统时钟对齐。
【解决方案3】:

实际上使用 BeginInvoke() 并不是一个坏主意。它可能看起来像这样:

private void Form1_Load(object sender, EventArgs e)
{
    thread = new Thread(() => Loop(this));
    thread.Start();
}

private void Loop(Form1 form)
{
    while (threadRunning && !form.IsDisposed)
    {
        MethodInvoker mi = delegate() { timeLabel.Text = /* Some text */ ; };
        BeginInvoke(mi);

        // Let sleep some time...
        Thread.Sleep(1);
    } 
}

private void Form1_FormClosing_1(object sender, FormClosingEventArgs e)
{
    threadRunning = false;
    thread.Join();
}

【讨论】:

  • bool threadRunningSleep() 可以合并为一个 ManualResetEvent
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-27
  • 1970-01-01
相关资源
最近更新 更多