线程访问.NET控件问题

时间:2013-03-23 14:41:07

标签: c# .net multithreading controls

我有两个看起来像这样的线程

pbDB_running = true; // start progress bar

Thread connectDb = new Thread(new ThreadStart(ConnectToDb));
Thread runProgress = new Thread(new ThreadStart(RunpbDB));

connectDb.Start();
runProgress.Start();

connectDb.Join();  //wait untill the connection is done

pbDB_running = false; //stop the progress bar

正如您可能已经猜到的那样,ConnectToDb用于建立与数据库的连接,而runpbDB正在接口上运行进度条。进度条(pbDB)是在设计视图上通过拖放创建的Windows.Forms控件。 runProgress线程正在运行RunpbDB(),如下所示:

private void RunpbDB()
{
    while (pbDB_running)
    {
        if (pbDB.Value == 100) pbDB.Value = 0;
        else pbDB.Value += 1;
    }
    pbDB.Value = 0;
} 

当两个线程启动时,我在RunpbDB()中得到以下异常:

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

我可以做些什么来克服这种情况?

6 个答案:

答案 0 :(得分:2)

您是否考虑过使用BackgroundWorker?这可能会让您的生活更轻松。您可以设置两个,一个用于数据库调用,另一个用于进度条。只需听取后台工作人员ProgressChangedRunWorkerCompleted事件。

有关MSDN

的更多信息

答案 1 :(得分:1)

使用Control.Invoke方法解决此问题。整个解决方案将成为

private void RunpbDB()
{
    while (pbDB_running)
    {
        Invoke((Action)(()=>{
        if (pbDB.Value == 100) pbDB.Value = 0;
        else pbDB.Value += 1;}));
    }
        Invoke((Action)(()=>{pbDB.Value = 0;});
} 

答案 2 :(得分:1)

您可以使用pbDB.InvokeRequired的内容,如果是这样,请调用pbDB.Invoke在UI线程上执行操作。

如果您知道它将始终在与UI线程不同的线程上完成,则不需要检查。

Here is a link to some code on this and other ways to accomplish this.

您还可以使用BackgroundWorker

答案 3 :(得分:1)

这是微软为其.NET技术强加的安全性。它主要发生在从单独的线程访问winforms元素时,即不在GUI winforms正在运行的主线程中。解决方案是为您的RunpbDB方法创建委托。请参阅此处的解决方案Best Way to Invoke Any Cross-Threaded Code?。在这里:How to update the GUI from another thread in C#?

答案 4 :(得分:1)

如果您无法访问.NET 4.0,只需让您的生活更轻松,并使用BackgroundWorker。如果您可以使用4.0+,请使用TPL。如果可以使用4.5,则可以使用新的async / await功能。 Stack Overflow上有很多例子。 Here是Stephen Cleary的一个链接,用来比较它们。

答案 5 :(得分:0)

VS团队在2.0中不鼓励涉及UI线程时的跨线程操作调用(之前可能出于安全原因。有两种方法可以解决这个问题。简单的方法是设置静态属性

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls

为false,然后从您的应用程序全局禁用此检查。但是这个解决方案并没有被任何人建议,甚至我也没有建议,因为它再次打开了安全漏洞。 另一种方法是,在方法中首先检查UI控件是否需要Invoke,如果是,则使用control的invoke方法再次调用当前方法然后返回。代码可以更好地清除我想说的内容

private void RunpbDB()
    {
        if (pbDB.InvokeRequired)
        {
            pbDB.Invoke(new Action(RunpbDB));
            return;
        }
        while (pbDB_running)
        {
            if (pbDB.Value == 100) pbDB.Value = 0;
            else pbDB.Value += 1;
        }
        pbDB.Value = 0;
    }