1

我看过一些指南,但没有一个能把我带到那里。我从未在杂货店发过话题、讨论过话题或看过话题,所以这可能是个问题。目前。我正在努力:

private void btnHUp_MouseDown(object sender, MouseEventArgs e)
    {
        {
            ThreadStart HUp = new ThreadStart(dothis);
            t = new Thread(HUp);
            t.Start();
        }
    }
         public void dothis()
{
        if (intHour < 23)
        intHour = intHour += intStep;            
        lblTimerHour.Text = intHour.ToString("00");
        }
         private void btnHUp_MouseUp(object sender, MouseEventArgs e)
         {

             t.Abort();
         }
    }

这让我 InvalidOperationException 在

 lblTimerHour.Text = intHour.ToString("00"); 

线。我读到了这意味着什么……它也可能是普通话,我有点明白出了什么问题的一般概念,但它非常模糊。如果你问我修复它的第一步,我会像头灯中的鹿一样看待你。我们只是在我的课堂上还没有走那么远。

4

4 回答 4

3

这里的问题是您尝试更新的标签归主线程所有(即 UI 在其上运行),这意味着只有该线程可以访问/更新它。因此,由于您在不同的线程中,您需要告诉 UI 线程为您更新标签。

像这样的东西会起作用:

Action updateLabel = () => lblTimerHour.Text = intHour.ToString("00");
lblTimerHour.BeginInvoke(updateLabel);

这样做是告诉 lblTimerHour 调用您在上面定义的操作 (updateLabel)。

于 2013-11-05T02:19:00.387 回答
2

请参阅这篇文章:如何从 C# 中的另一个线程更新 GUI?

lblTimerHour.Invoke((MethodInvoker)delegate {
    //Do what you need to do with the label
    lblTimerHour.Text = intHour.ToString("00");
});

编辑

这应该可以解决问题:

public void dothis()
{
    do
    {
        if (intHour < 23)
            intHour = intHour += intStep;     

        lblTimerHour.Invoke((MethodInvoker)delegate {   
            //Update the label from the GUI thread    
            lblTimerHour.Text = intHour.ToString("00");
        });

        //Pause 1 sec. Won't freeze the gui since it's in another thread
        System.Thread.Sleep(1000);

    }while(true); //Thread is killed on mouse up
}
于 2013-11-05T02:23:23.903 回答
1

如果你想每隔 X 时间更新一次 UI,那么已经有现有的工具可以做到这一点;aTimer将完全按照您的意愿进行操作,并且与创建一个大部分时间都在打盹的新线程相比,它会更高效、更容易编写代码。此外,中止线程是一个非常糟糕的迹象。不惜一切代价避免它。

首先创建定时器并在构造函数中进行配置:

private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
private int hour = 0;
private int step = 0;
public Form1()
{
    InitializeComponent();

    timer.Tick += timer_Tick;
    timer.Interval = 1000;
}

Tick事件在它滴答作响的时候做任何应该做的事情。

private void timer_Tick(object sender, EventArgs e)
{
    if (hour < 23)
    {
        hour += step;
        lblTimerHour.Text = hour.ToString("00");
    }
}

然后在您希望计时器开始计时时启动计时器并在您希望计时器停止时停止计时器:

private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
    timer.Start();
}

private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
    timer.Stop();
}

计时器将自动确保Tick事件处理程序在 UI 线程中运行,并且在等待下一个事件发生时它不会阻塞 UI 线程(或任何其他线程),它什么也不做。

于 2013-11-05T20:24:20.370 回答
1

好吧,让我们来看看你已经拥有什么。

首先,我看到你这样做了。

private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
  ThreadStart HUp = new ThreadStart(dothis);
  t = new Thread(HUp);
  t.Start();
}

虽然这肯定不是最新鲜的东西,但它仍然可以工作。如果你想要一些更新鲜的食材,那么你可以用这个代替。

private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
  Task.Factory.StartNew(dothis);
}

其次,我看到了这一点。

public void dothis()
{
  if (intHour < 23) intHour = intHour += intStep;            
  lblTimerHour.Text = intHour.ToString("00");
}

这里的问题是您试图从主 UI 线程以外的线程更新 UI 控件。您会看到 UI 控件具有所谓的线程关联性。它们只能从创建它们的线程访问。你所拥有的将导致各种不可预知的问题,甚至包括在时空中撕裂一个整体。

更好的选择是这样做。

public void dothis()
{
  while (intHour < 23) 
  {
    intHour = intHour += intStep;       
    lblTimerHour.Invoke((Action)(
      () =>
      {
        lblTimerHour.Text = intHour.ToString("00");
      }));
  }     
}

我假设你错过了循环,所以我添加了它。虽然我不能说我个人对这种东西有品味,但它更容易下咽。这里真正的问题是工作线程真的没有做很多有用的工作。然后最重要的是,我们必须使用笨拙的编组操作将结果传输回 UI 线程。它不漂亮,但它会起作用。

最后,这让我想到了这一点。

private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
   t.Abort();
}

您正试图中止一个非常不可取的线程。问题是它在不可预测的时间从线程中抽出控制。该线程可能正在写入数据结构,这会破坏它。这实际上是一个非常糟糕的问题,因为在从调用堆栈上的任何一个帧操作的过程中的任何数据结构都可能处于不一致的状态。这包括您未编写的代码。这就是为什么很难说你这样做可能会或可能不会破坏什么。

您需要考虑的是使用合作取消机制。这包括使用CancellationTokenSourceCancellationToken。这是我们将所有内容放在一起后的样子。

private CancellationTokenSource cts = null;

private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
  cts = new CancellationTokenSource();
  Task.Factory.StartNew(() => dothis(cts.Token));
}

private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
   cts.Cancel();
}

public void dothis(CancellationToken token)
{
  while (!token.IsCancellationRequested) 
  {
    intHour += intStep;       
    lblTimerHour.Invoke((Action)(
      () =>
      {
        lblTimerHour.Text = intHour.ToString("00");
      }));
    Thread.Sleep(1000);
  }     
}

这样做是表明工作线程应该自行正常关闭。这使工作线程有机会在最终自行终止之前进行整理。

于 2013-11-05T02:43:41.917 回答