“跨线程操作无效” “backgroundWorker1_DoWork”错误活动..! [英] "Cross-thread operation not valid" Error on "backgroundWorker1_DoWork" Event ..!

查看:128
本文介绍了“跨线程操作无效” “backgroundWorker1_DoWork”错误活动..!的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家好,

我创建了一个在form_load上执行某些功能的表单,同时我想显示一个表单(如请稍候)。

我用后台工作者

我的代码是: -

  private   void  QuestionForm_Load( object  sender,EventArgs e)
{
WP = new WorkerProgress(); // 此处WorkerProgress是一个显示消息的表单(请稍候)。
backgroundWorker1.RunWorkerAsync();
}
私有 void backgroundWorker1_DoWork( object sender,System.ComponentModel.DoWorkEventArgs e)
{
WP.Show();
for int i = 1 ; i < = Noq; i ++)
{
Button lbl = new Button();
lbl.Text = i.ToString();
lbl.AutoSize = true ;
lbl.AutoSizeMode = AutoSizeMode.GrowAndShrink;
lbl.Font = new 字体( Comic Sans MS 9 ,FontStyle.Bold);
lbl.BackColor = Color.AntiqueWhite;
lbl.Click + = new EventHandler(lbl_Click);
tableLayoutPanel1.Controls.Add(lbl); // 错误在这里跨线程操作无效:从线程以外的线程访问控制'tableLayoutPanel1'创建于。
}
generateRnd(); // 这是函数
getQues(); // 这是一个函数
SetFirstQues(); // 这是一个函数
label2.Text = mm。 ToString( 00)+ + ss.ToString( 00);
setToolTip(); // 这是一个函数
}


private void backgroundWorker1_RunWorkerCompleted( object sender,System.ComponentModel.RunWorkerCompletedEventArgs e)
{
WP.Dispose();
}





如何解决这个错误?



谢谢你提前....



Regard

Jayanta ...

解决方案

Hello Mam,



当我们启动基于表单的应用程序时,应用程序在单个UI线程上启动。

现在如果我使用它的一些控件执行一些操作。假设,在按钮单击时,执行一些计算。如果计算很小就可以了,但随着要完成的工作的增加,您的表单将变为白色并呈现无响应的消息。这是因为UI线程无法同时呈现内容并执行计算。



所以我们需要从主线程转移负载,在其他一些后台线程上执行该操作并将结果返回给UI。同时你也会知道你工作状态的进展情况。



我会给你一个小小的演示,借助它你可以模拟场景根据您的要求。



表格代码:



  public   partial   class  Form1:Form 
{
public Form1()
{
InitializeComponent();
}

private void button1_Click( object sender,EventArgs e) /// /此按钮启动后台工作线程
{
Class1 c = new Class1(label1); /// /初始化类并将其传递给标签,以便您可以按%$ b $显示进度b c.init_background_worker()。RunWorkerAsync(); /// 初始化后台工作程序并启动它。

}

private void button2_Click( object sender,EventArgs e)
{
MessageBox.Show( Hello); /// /此按钮被给出,以便您可以在后台工作人员操作时反复按此按钮。您会注意到操作平稳且无响应性
}
}







现在在另一个班级:



 class Class1 
{
static BackgroundWorker bw;
标签L;
public Class1(Label l)
{
L = l;

}


public BackgroundWorker init_background_worker()
{

bw = new BackgroundWorker();
bw.DoWork + =新的DoWorkEventHandler(dowork);
bw.ProgressChanged + = new ProgressChangedEventHandler(changeinprogress);
bw.RunWorkerCompleted + = new RunWorkerCompletedEventHandler(workdone);
bw.WorkerReportsProgress = true; ///这告诉后台工作者报告进度。
返回bw;
}

public void dowork(object sender,DoWorkEventArgs e)
{
for(int i = 0; i< 10000; i ++)
{
Thread.Sleep(200);
if(i == 999)
{
bw.ReportProgress(100);


}
bw.ReportProgress(i); ////报告进度方法传递给索引i,因此当循环迭代时,i的值可以作为单位百分比

}
}

public void changeinprogress(object sender,ProgressChangedEventArgs e)
{
L.Text = e.ProgressPercentage.ToString()+%; ////显示正在进行的更改,甚至在调用ReportProgress()方法时触发。

}

public void workdone(object sender,RunWorkerCompletedEventArgs e)
{
L.Text =Done; ////整个操作完成后,hello会显示在标签上。

}


}





< br $>
}





谢谢,

- Rahul


你好Jayanta,

我正在阅读你对OriginalGriff的讨论,我想知道真正的问题是微软TableLayoutPanel的糟糕性能。如果您遇到重绘问题,那么在控件上启用双缓冲会加快速度。我发现在.NET版本2上差异非常明显。



DoubleBuffered 属性是 protected 并将其设置为 true 您必须创建派生类并在构造函数中进行更改。



这是我的库中有用的代码。

  ///   <  摘要 < span class =code-summarycomment>>  
/// DoubleBuffered = true显着提高重绘性能
/// < / summary >
/// < 备注 > 由于false会导致性能不佳,因此很难
/// 了解Microsoft将其作为默认值的原因。< / remarks >
public class ImprovedTableLayoutPanel:System.Windows.Forms.TableLayoutPanel {
public ImprovedTableLayoutPanel()
base (){
DoubleBuffered = true ;
}
}





双缓冲,如果您不知道,改变方式屏幕已更新。它不是直接绘制到视频适配器,而是分两步完成。首先,将更改写入内存缓冲区中的图像,然后使用称为位blitting的技术将图像复制到适配器。由于写入主内存很快,并且视频适配器非常擅长点位,两步双缓冲过程通常比直接方式更好。



让我知道这是否有用,或者如果你需要更多信息,那么就问问我会尽力帮助。



Alan。


< blockquote>你不能完全从它们创建的线程访问控件:UI线程。

你试图从你的BackgroundWorker访问它们,这完全是一个不同的线程。

使用Invoke方法访问它们,或者通过BackgroundWorker.ReportProgress方法将代码移动到UI线程中。



先生,抱歉缺少信息..

我测试了所有方法循环30(意味着Noq = 30)。

如果我用100做,那么它会变得更长时间(超过4分钟)执行...

然后问题就会出现.... :-(

如何解决这个问题???




快速测试给了我不同的结果 - 但这是可以预料的。每次按下按钮并添加另外30个按钮,我得到大约300毫秒,增加150毫秒,所以你的布局面板可能与我的不同。



但是,你考虑过你在这做什么吗?每次添加一个控件时,布局面板都有大量的工作要做......



所以,再试一次,还有你的秒表代码,看看简单的mod会发生什么:

秒表s1 = 秒表(); 
s1.Start();
tableLayoutPanel1.SuspendLayout();
for int i = 1 ; i < = Noq; i ++)
{
Button lbl = new Button();
lbl.Text = i.ToString();
lbl.AutoSize = true ;
lbl.AutoSizeMode = AutoSizeMode.GrowAndShrink;
lbl.Font = new 字体( Comic Sans MS 9 ,FontStyle.Bold);
lbl.BackColor = Color.AntiqueWhite;
lbl.Click + = new EventHandler(lbl_Click);
tableLayoutPanel1.Controls.Add(lbl); // 错误在这里跨线程操作无效:从线程以外的线程访问控制'tableLayoutPanel1'创建于。
}
tableLayoutPanel1.ResumeLayout();
s1.Stop();



您应该会发现您的数字会立即下降到一致的较小值,因为您只会导致布局面板重新计算一次。


Hi all,
I create a form which have some function perform on the form_load, at the meantime I want to show a form(like "please wait").
I used Background Worker.
My code is :-

private void QuestionForm_Load(object sender, EventArgs e)
        {
            WP = new WorkerProgress();//here WorkerProgress is a form to display the message("Please wait").
            backgroundWorker1.RunWorkerAsync();           
        }
 private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            WP.Show();
            for (int i = 1; i <= Noq; i++)
            {
                Button lbl = new Button();
                lbl.Text = i.ToString();
                lbl.AutoSize = true;
                lbl.AutoSizeMode = AutoSizeMode.GrowAndShrink;
                lbl.Font = new Font("Comic Sans MS", 9, FontStyle.Bold);
                lbl.BackColor = Color.AntiqueWhite;
                lbl.Click += new EventHandler(lbl_Click);
                tableLayoutPanel1.Controls.Add(lbl); //error is here "Cross-thread operation not valid: Control 'tableLayoutPanel1' accessed from a thread other than the thread it was created on."
            }
            generateRnd();//this is a function
            getQues();//this is a function
            SetFirstQues();//this is a function
            label2.Text = mm.ToString("00") + ":" + ss.ToString("00");
            setToolTip();//this is a function
        }
        

        private void backgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
            WP.Dispose();
        }



How to solve this Error ?

Thanks in advanced....

Regard
Jayanta...

解决方案

Hello Mam,

When ever we start a form based application, the application starts on a single UI thread.
Now if i perform some operation using some of its controls. Suppose, on button click, perform some calculation. If the calculation is small its okay, but as the work to be done grows, then your form will turn white and render a not responding message. This is because the UI thread cant simultaneously render contents and perform calculation.

So we need to divert the load from the main thread, perform that operation on some other background thread and the result will be returned to the UI. Meanwhile you will also come to know the progress of your work status.

I will show you a small demo with the help of which may be you can simulate the scenario according to your demand.

FORM code:

public partial class Form1 : Form
   {
       public Form1()
       {
           InitializeComponent();
       }

       private void button1_Click(object sender, EventArgs e) ////This button starts the background worker thread
       {
           Class1 c = new Class1(label1); ////Initialize the class and pass it the label so that you can display the progress in terms of %
           c.init_background_worker().RunWorkerAsync(); ///initialize the  background worker and start it.

       }

       private void button2_Click(object sender, EventArgs e)
       {
           MessageBox.Show("Hello");         //// this button is given so that you can press this button again and again while the background worker operates. You will notice there is a smooth operation and no non responsiveness
       }
   }




Now in a different class:

class Class1
   {
      static BackgroundWorker bw;
      Label L;
       public Class1(Label l)
       {
           L = l;

       }


       public BackgroundWorker init_background_worker()
       {

           bw = new BackgroundWorker();
           bw.DoWork += new DoWorkEventHandler(dowork);
           bw.ProgressChanged += new ProgressChangedEventHandler(changeinprogress);
           bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workdone);
           bw.WorkerReportsProgress = true; ///this tells the background worker to report progress.
           return bw;
       }

       public void dowork(object sender, DoWorkEventArgs e)
       {
           for (int i = 0; i < 10000; i++)
           {
               Thread.Sleep(200);
               if (i == 999)
               {
                   bw.ReportProgress(100);


               }
                   bw.ReportProgress(i); ////the report progress method is passed the index i, so that as the loop iterates the value of i can be taken as a unit percentage

           }
      }

       public void changeinprogress(object sender,ProgressChangedEventArgs e)
       {
           L.Text = e.ProgressPercentage.ToString() + "%"; ////displayes the change in progress, this even is triggered when you call the ReportProgress() method.

       }

       public void workdone(object sender, RunWorkerCompletedEventArgs e)
       {
           L.Text = "Done"; //// when entire operation completes hello is displayed on the label.

       }


   }




}


Thanks,
- Rahul


Hi Jayanta,
I was reading the discussion you were having with OriginalGriff and I wonder if the real problem is the poor performance of Microsoft's TableLayoutPanel. If you are having issues with redraw then enabling double buffering on the control will speed it up. I have found that on .NET version 2 the difference is really noticeable.

The DoubleBuffered property is protected and to set it to true you have to create a derived class and make the change in the constructor.

This is the code I have in my library of useful stuff.

/// <summary>
/// DoubleBuffered = true improves redraw performance dramatically
/// </summary>
/// <remarks>As false gives such poor performance it is difficult
/// to understand why Microsoft made it the default.</remarks>
public class ImprovedTableLayoutPanel : System.Windows.Forms.TableLayoutPanel {
  public ImprovedTableLayoutPanel()
    : base() {
    DoubleBuffered = true;
  }
}



Double buffering, if you don't know, changes the way that the screen is updated. Instead of drawing directly to the video adapter, it will now be done in two steps. First the changes are written to an image in a memory buffer and then the image is copied to the adapter using a technique called bit blitting. As writing to main memory is fast, and video adapters are very good at bit blitting, the two step double buffered process is usually better then the direct way.

Let me know if this is useful, or if you need more information then just ask and I'll try to help.

Alan.


You cannot access controls exactly from the thread that they were created on: the UI thread.
You are trying to access them from your BackgroundWorker, which is a different thread entirely.
Either use the Invoke method to access them, or move the code into the UI thread via the BackgroundWorker.ReportProgress method.

"Sir, sorry for the missing info..
I tested this all methods loops with 30 (means Noq=30).
If I do it with 100 then it will be getting longer time(more then 4 min.) to execute ...
then the problem will arise .... :-(
How to solve this???"


A quick test gives me different results to you - but that is to be expected. I get around 300ms, increasing by 150ms odd each time I press the button and add another 30 buttons, so it's likely that your layout panel is setup different to mine.

However, have you considered what you are doing here? Each time you add a control, the layout panel has a whole pile of work to do...

So, try again, still with your Stopwatch code in there, and see what happens with a simple mod:

Stopwatch s1 = new Stopwatch();
s1.Start();
tableLayoutPanel1.SuspendLayout();
for (int i = 1; i <= Noq; i++)
    {
    Button lbl = new Button();
    lbl.Text = i.ToString();
    lbl.AutoSize = true;
    lbl.AutoSizeMode = AutoSizeMode.GrowAndShrink;
    lbl.Font = new Font("Comic Sans MS", 9, FontStyle.Bold);
    lbl.BackColor = Color.AntiqueWhite;
    lbl.Click += new EventHandler(lbl_Click);
    tableLayoutPanel1.Controls.Add(lbl); //error is here "Cross-thread operation not valid: Control 'tableLayoutPanel1' accessed from a thread other than the thread it was created on."
    }
tableLayoutPanel1.ResumeLayout();
s1.Stop();


You should find your numbers drop immediately to a consistent, smaller, value because you are only causing the layout panel to recalculate the once.


这篇关于“跨线程操作无效” “backgroundWorker1_DoWork”错误活动..!的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆