【问题标题】:Error Cross-thread operation not valid: Control 'CameraViewVS' accessed from a thread other than the thread it was created on. parallel.for错误跨线程操作无效:控件“CameraViewVS”从创建它的线程以外的线程访问。并行.for
【发布时间】:2012-07-12 17:52:56
【问题描述】:

我有一个计时器来每次验证一个条件,如果条件得到验证,只显示一次弹出表单。我想并行验证所有实例,所以我使用了parallel.for,但我有这个错误“跨线程操作无效:控制'CameraViewVS'从创建它的线程以外的线程访问。”在“frm.WindowState = FormWindowState.Normal;”行中

这是我的代码:

public void timer1_Tick(object source, EventArgs e)               
{
    Parallel.For(0, nbre, l =>
        {
            cameraInstanceList[l].Start();
            if (cameraInstanceList[l].MoveDetection == true)
            {
                //show the the form S once 
                foreach (Form S in Application.OpenForms)
                {
                    var frm = S as Formes.CameraViewVS;
                    if (frm != null && frm.IP == cameraInstanceList[l].adresse)
                    {
                        cameraInstanceList[l].MoveDetection = false;
                        frm.WindowState = FormWindowState.Normal;
                        frm.Activate();
                        return;
                    }
                }

                f1 = new Formes.CameraViewVS(cameraInstanceList[l],
                adresseIPArray[l]);
                f1.Show(this);

            }

        }               
        );

【问题讨论】:

    标签: multithreading c#-4.0


    【解决方案1】:

    WinForm 对象实例上的大多数属性都需要从创建它们的线程中访问。您可以使用 Control.InvokeRequired 属性来确定是否需要使用控件(或表单)的 Invoke 方法来执行 UI 线程上的代码。

    在主 UI 线程上而不是在任何线程池线程上创建大多数 WinForm 控件也是一个好习惯。在 WinForms 应用程序中,您可以使用 SynchronizationContext 确保在 UI 线程上调用某些代码,例如创建表单。

    编辑:已更改,以便在检测到移动后该方法不会返回。

    public void timer1_Tick(object source, EventArgs e)                         
    {          
        // assume this is being called on the UI thread, and save the thread synchronization context
        var uiContext = SynchronizationContext.Current;
    
        Parallel.For(0, nbre, l =>          
            {          
                while (true)
                {
                    Thread.Sleep(250);  // <--- sleep for 250 ms to avoid "busy" wait
    
                    cameraInstanceList[l].Start();          
                    if (cameraInstanceList[l].MoveDetection == true)          
                    {       
                        // capture instances used in closures below
                        var cameraInstance = cameraInstanceList[l];
                        var ipAdresse = adresseIPArray[l];
    
                        //show the the form S once                             
                        foreach (Form S in Application.OpenForms)                            
                        {
                            var frm = S as Formes.CameraViewVS; 
                            if (frm != null)
                            {
                                // create delegate to be invoked on form's UI thread.
                                var action = new Action(() =>
                                    {
                                        if (frm.IP == cameraInstance.adresse) 
                                        { 
                                            cameraInstance.MoveDetection = false; 
                                            frm.WindowState = FormWindowState.Normal; 
                                            frm.Activate(); 
                                        }
                                    };
    
                                if (frm.InvokeRequired)
                                    frm.Invoke(action);  
                                else
                                    action();
    
                                continue; // <--- go back to the top of the while loop
                                          //      and wait for next detection
                            }
                        }
    
                        // create delegate to create new form on UI thread.
                        var createNewFormCallback = new SendOrPostCallback((o) =>
                            {
                                f1 = new Formes.CameraViewVS(cameraInstance, ipAdresse); 
                                f1.Show(this);  
                            };    
    
                        // and invoke the delegate on the ui thread
                        uiContext.Send(createNewFormCallback, null); 
                    }            
                } 
            }
            );
    }
    

    【讨论】:

    • 它错过了 uiContext.Send(createNewFormCallback, xxx) 中的一个参数,我添加了“line” + l.ToString(),但是当我执行它时只显示一个弹出窗口,我比较新在使用线程,所以如果你能给我一些好的教程的链接,再次感谢你
    • @Ostorlabi 感谢您指出这一点。第二个参数应该是null。我建议您使用调试器在MovedDetection == true 之后放置断点,或者使用 Debug.WriteLine 记录执行流程以查看哪些有效,哪些无效。
    • 我添加了 Debug.WriteLine("error");在 MovedDetection == true 之后,记录流程但我没有看到任何内容,是否保存了文件?
    • @Ostorlabi 如果您在使用 Visual Studio 调试器中运行它,您应该能够在输出窗口中看到它。请参阅 support.microsoft.com/kb/815788technet.microsoft.com/en-us/sysinternals/bb896647 上的独立 DebugView
    • @Thomas:谢谢,我看到了输出窗口,它只在第一次移动时显示消息“错误”,即使对于另一个实例,movedDetection == true 或者也同样的实例,它不会在其他时间显示“错误”。然而,当我在没有并行的情况下使用初始代码进行测试时,每次移动时都会显示“错误”。
    【解决方案2】:

    Thomas 非常接近正确答案,因为每个控件都在不同的线程中运行。您应该只编写代码用于控件正在使用的资源的上下文切换 线程 ..别担心你在 c 语言中有很多功能。只需使用 BeginInvoke 和 Invoke,我希望你能够解决你的问题。用这个代替你的旧代码块 ..

                     var action = new Action(() =>
                                {
                                    if (frm.IP == cameraInstance.adresse) 
                                    { 
                                        cameraInstance.MoveDetection = false; 
                                        frm.WindowState = FormWindowState.Normal; 
                                        frm.Activate(); 
                                    }
                                };
    
                            if (frm.InvokeRequired)
                                frm.BeginInvoke(action);  
                            else
                                frm.Invoke(action);
    

    【讨论】:

    • 我写了,这个,它可以显示多个弹出窗口,但是它们与移动不同步的问题,我解释说,当一个相机中有移动时它会显示但如果第二个和第三个摄像头在第一个摄像头同时移动,则在我关闭第一个摄像头之前,不会显示第二个和第三个摄像头。但是如果我不使用parallel.for(只是简单的boucle for),如果你看到我的第一个代码,它可以正常工作并且比parallel.for更快。
    猜你喜欢
    • 2011-01-15
    • 2016-05-21
    • 2016-06-11
    • 2015-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多