0

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

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.

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

4

6 回答 6

2

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

有关MSDN的更多信息

于 2013-03-23T14:49:29.207 回答
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;});
} 
于 2013-03-23T14:46:45.333 回答
1

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

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

这是有关此操作和其他方法的一些代码的链接。

你也可以使用BackgroundWorker

于 2013-03-23T14:46:58.667 回答
1

这是微软为其 .NET 技术强加的安全措施。它基本上发生在您从单独的线程访问 winforms 元素时,即不在运行 GUI winforms 的主线程中。解决方案是为您的RunpbDB方法创建一个委托。请参阅此处的解决方案调用任何跨线程代码的最佳方法?. 也在这里:如何从 C# 中的另一个线程更新 GUI?

于 2013-03-23T14:51:05.163 回答
1

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

于 2013-03-23T14:51:49.763 回答
0

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

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls

设置为 false 然后从您的应用程序全局禁用此检查。但是任何人都没有建议这种解决方案,甚至我也没有建议,因为它再次打开了安全漏洞。另一种方式是,在方法中先检查UI控件是否需要Invoke,如果需要则使用控件的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;
    }
于 2013-03-23T15:04:38.367 回答