For循环多线程传递上限

时间:2014-02-05 14:15:55

标签: c# multithreading for-loop begininvoke

我们今天在课堂上学习多线程,我们遇到了一个非常奇怪的错误。在我们的新线程中执行for循环时,for循环的上限不断传递。该线程被杀死,但随后会出现另一个值并结束另一个线程。

出于调试错误的目的,我将上限更改为90以避免进度条上的OutOfRange异常。

在将计数器输出到进度条并更新进度条的同时,我在输出窗口中得到了这个。

while updating progress bar

如果我在进度条(pbLoad.Value = i;)上注释了更新,我在输出窗口中得到了这个

while not updating the progress bar

我尝试将循环更改为i<101,并尝试移动到i++所在的位置,但没有区别

编辑:这来自BeginInvoke。当我将其切换到Invoke时,它可以工作,但是当我尝试使用取消按钮时,我将陷入僵局。

以下是代码:

public partial class Form1 : Form
    {
        Thread backgroundThread;
        bool stopExecution = false;

        public Form1()
        {
            InitializeComponent();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            stopExecution = false;
            btnStart.Enabled = false;
            backgroundThread = new Thread(DoDomethingThatTakesAWhile);
            backgroundThread.Start();
        }

        private void DoDomethingThatTakesAWhile()
        {
            for (int i = 0; i <= 100; i++)
            {
                if (!stopExecution)
                {
                    Thread.Sleep(100);

                    if (pbLoad.InvokeRequired)
                    {
                        MethodInvoker myMethod
                            = new MethodInvoker(
                                delegate
                                {
                                    if (!stopExecution)
                                    {
                                        pbLoad.Value = i;
                                        Debug.WriteLine(i); //i to output window                                        
                                    }
                                });
                        pbLoad.BeginInvoke(myMethod);
                    }
                    else
                    {
                        pbLoad.Value = i;
                    }
                }
                else
                {
                    break;
                }
            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            //backgroundThread.Abort();
            stopExecution = true;
            backgroundThread.Join();

            pbLoad.Value = 0;
            btnStart.Enabled = true;
        }
    }

2 个答案:

答案 0 :(得分:1)

您正在使用BeginInvoke明确开启比赛的可能性。我建议同步调用。

此外,您正在捕获i,而不是其值。这很生气,只是因为你正在睡觉而偶然起作用。

任何一项更改都可以解决问题。做他们两个。

如果可以的话,废除这种低级别的同步使用并使用async / await。

答案 1 :(得分:1)

当您致电MethodInvoke时,此时不会发生,但某段时间

在您的方案中,您有可能发生以下情况:

  • 最终执行了调用的代码;
  • 循环已经完成(i变为101)
  • 您正在直接访问i并阅读101。

要修复它,您可以复制i(通过将其作为参数传递给调用的方法):

pbLoad.BeginInvoke(new Action<int>(a =>
{
    if (!stopExecution)
    {
        pbLoad.Value = a;
        Debug.WriteLine(a); //a to output window                                        
    }
}), new object[] { i });

P.S:您不需要检查InvokeRequired,除非您打算直接调用DoDomethingThatTakesAWhile方法,我认为并非如此。