如何解决C#.NET中的“跨线程操作无效”错误? [英] How to resolve “Cross-thread operation not valid ” error in C# .NET?

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

问题描述

我在winform应用程序中使用加载屏幕。所以我在这里



1.main form(frmBilling)

2.splashscreen form(frmsplash)和

3.class splashWorker.cs



这是splashWorker.cs



  public   class  splashWorker 
{
public event EventHandler< splashWorkerEventArgs> ProgressChanged;
public event EventHandler HardWorkDone;
public void DoHardWork()
{
for int i = 1 ; i < = 100 ; i ++)
{
for int j = 1 ; j < = 500000 ; j ++)
{
Math.Pow(i,j);
}
this .OnProgressChanged(i);
}

this .OnHardWorkDone();
}

private void OnProgressChanged( int progress)
{
var handler = this .ProgressChanged;
if (handler!= null
{
handler( this new splashWorkerEventArgs(progress));
}
}

私有 void OnHardWorkDone( )
{
var handler = this .HardWorkDone;
if (handler!= null
{
handler( this ,EventArgs.Empty);
}
}
}
public class splashWorkerEventArgs :EventArgs
{
public splashWorkerEventArgs( int progress)
{
this .Progress = progress;
}

public int 进度
{
get ;
private set ;
}
}





现在,这是启动画面中的代码。(有一个progressBar( progressBar1)以这种形式。)



  public   partial   class  frm_splash:表单
{
private 委托 void ProgressDelegate( int 进展);

private ProgressDelegate del;
public frm_splash()
{
InitializeComponent();
this .progressBar1.Maximum = 100 ;
del = this .UpdateProgressInternal;
}
私有 void UpdateProgressInternal( int 进度)
{
if this .Handle == null
{
return ;
}

.progressBar1.Value = progress;
}
public void UpdateProgress( int progress)
{
this .Invoke(del,progress);
}
}





最后,这是我要在此启动画面后显示的主要表单加载。



  public   partial   class  frm_billingMain:Form 
{
private frm_splash splashScreen ;
private bool done = false ;

public frm_billingMain( string get_uid)
{
InitializeComponent();
this .Load + = new EventHandler(HandleFormLoad);
this .splashScreen = new frm_splash();
c.uid = get_uid;
}
private void HandleFormLoad( object sender,EventArgs e)
{
this .Hide();

线程thread = new 线程( new ThreadStart( .ShowSplashScreen));
thread.Start();

splashWorker worker = new splashWorker();
worker.ProgressChanged + =(o,ex)= >
{
.splashScreen.UpdateProgress(ex.Progress);

};

worker.HardWorkDone + =(o,ex)= >
{
done = ;
this .Show();
};

worker.DoHardWork();
}
私有 void ShowSplashScreen()
{
splashScreen.Show();
while (!done)
{
Application.DoEvents();
}
splashScreen.Close();
this .splashScreen.Dispose();
}



当我运行表单时,我遇到异常:





## InvalidOperationException未处理。



跨线程操作无效:访问控制'frm_splash'来自其创建的线程以外的线程。




此错误显示在 ShowSplashScreen() frm_Billingmain



  private   void  ShowSplashScreen()
{
splashScreen.Show(); ** // 异常指向此处**
while (!done)
{
Application.DoEvents();
}
splashScreen.Close();
this .splashScreen.Dispose();
}



我试了很多东西来解决,没有什么事情发生。任何帮助将不胜感激。



谢谢。

解决方案

嗯,这是完全正确的说法:

控制'frm_splash'从其创建的线程以外的线程访问



它已创建在你的frm_billingMain构造函数中的UI线程上:

  this  .splashScreen =  new  frm_splash(); 

你在

ShowSplashScreen中使用它在一个单独的线程中调用:

线程thread =  new 线程( new  ThreadStart( this  .ShowSplashScreen)); 
thread.Start();



您无法从创建它们的UI线程以外的任何线程访问控件。

你可以使用Invoke来解决这个问题,但是说实话,我会将大部分代码转储为不是一个好主意。任何时候你最终使用Application.DoEvents它通常意味着你的代码存在真正的问题,并且在线程环境中使用它意味着存在严重错误。



我首先要看一下闪屏上的一些文章: Google启动画面在CodeProject上 [ ^ ]并看看他们是如何做到的 - 你们已经结束了很多事情! :笑:


I'm using a loading screen in my winform application. So here I've

1.main form (frmBilling)
2.splashscreen form (frmsplash) and a
3.class splashWorker.cs

This is the splashWorker.cs

public class splashWorker
    {
       public event EventHandler<splashWorkerEventArgs> ProgressChanged;
        public event EventHandler HardWorkDone;
        public void DoHardWork()
        {
            for (int i = 1; i <= 100; i++)
            {
                for (int j = 1; j <= 500000; j++)
                {
                    Math.Pow(i, j);
                }
                this.OnProgressChanged(i);
            }

            this.OnHardWorkDone();
        }

        private void OnProgressChanged(int progress)
        {
            var handler = this.ProgressChanged;
            if (handler != null)
            {
                handler(this, new splashWorkerEventArgs(progress));
            }
        }

        private void OnHardWorkDone()
        {
            var handler = this.HardWorkDone;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }
    public class splashWorkerEventArgs: EventArgs
    {
        public splashWorkerEventArgs(int progress)
        {
            this.Progress = progress;
        }

        public int Progress
        {
            get;
            private set;
        }
    }



and now, This is the code in splash screen.(There is a progressBar(progressBar1) in this form.)

public partial class frm_splash : Form
  {
    private delegate void ProgressDelegate(int progress);

    private ProgressDelegate del;
    public frm_splash()
    {
        InitializeComponent();
        this.progressBar1.Maximum = 100;
        del = this.UpdateProgressInternal;
    }
    private void UpdateProgressInternal(int progress)
    {
        if (this.Handle == null)
        {
            return;
        }

        this.progressBar1.Value = progress;
    }
    public void UpdateProgress(int progress)
    {
        this.Invoke(del, progress);
    }
}



At last, this is the main form that I want to show after this splash screen loads.

public partial class frm_billingMain : Form
{
    private frm_splash splashScreen;
    private bool done = false;        

    public frm_billingMain(string get_uid)
    {
        InitializeComponent();
        this.Load += new EventHandler(HandleFormLoad);
        this.splashScreen = new frm_splash();
        c.uid = get_uid;
    }
    private void HandleFormLoad(object sender, EventArgs e)
    {
        this.Hide();

        Thread thread = new Thread(new ThreadStart(this.ShowSplashScreen));
        thread.Start();

        splashWorker worker = new splashWorker();
        worker.ProgressChanged += (o, ex) =>
        {
            this.splashScreen.UpdateProgress(ex.Progress);

        };

        worker.HardWorkDone += (o, ex) =>
        {
            done = true;
            this.Show();
        };

        worker.DoHardWork();
    }
    private void ShowSplashScreen()
    {
        splashScreen.Show();
        while (!done)
        {
            Application.DoEvents();
        }
        splashScreen.Close();
        this.splashScreen.Dispose();
    }


What is happens is , when I run the form , I'm getting an exception :


##InvalidOperationException was unhandled.

"Cross-thread operation not valid: Control 'frm_splash' accessed from a thread other than the thread it was created on."


This error was showing in ShowSplashScreen() in frm_Billingmain

private void ShowSplashScreen()
        {
            splashScreen.Show(); **//The exception is pointing here**
            while (!done)
            {
                Application.DoEvents();
            }
            splashScreen.Close();
            this.splashScreen.Dispose();
        }


I tried a lot to solve and nothing happens well. Any help will be appreciated.

Thanks.

解决方案

Well, it is exactky what is says:

Control 'frm_splash' accessed from a thread other than the thread it was created on


It was created on the UI thread in your frm_billingMain constructor:

this.splashScreen = new frm_splash();

And you are using it in
ShowSplashScreen which is called in a separate thread:

Thread thread = new Thread(new ThreadStart(this.ShowSplashScreen));
thread.Start();


You can't access controls from any thread other than the UI thread they were created on.
You could use Invoke to get round this, but in all honesty I'd dump most of that code as "not a good idea". Any time you end up using Application.DoEvents it normally means there is a real problem with your code, and to use it in a threaded environment means there is something seriously wrong.

I would start by looking at some of the many articles here on splash screens: Google splash screens on CodeProject[^] and see how they do it - you are over complicating things a lot! :laugh:


这篇关于如何解决C#.NET中的“跨线程操作无效”错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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