none
replace ForEach with Parallel.ForEach RRS feed

  • Question

  • Hi,

    Is it possible to convert this code to a Parallel.ForEach ?

    private void LoadTreeViewFoldersChilds(TreeNode parentNode)
            {
                DataClickerFolder parentFolder = (DataClickerFolder)parentNode.Tag;
                DataClickerFolders _folders = this.Parent.SQLLiteApp.GetFolders("WHERE PARENT = '" + parentFolder.Guid + "'");
    
                foreach (DataClickerFolder _qbq in _folders.Items)
                {
                    if (_qbq.FolderType == FolderTypes.Folder)
                    {
                        TreeNode _node = parentNode.Nodes.Add("", _qbq.Name, "Folder", "FolderSelected");
                        _node.Tag = _qbq;
                        // Read child folders
                        LoadTreeViewFoldersChilds(_node);
                    }
                    else
                    {
                        TreeNode _node = parentNode.Nodes.Add("", _qbq.Name, "Query", "QuerySelected");
                        _node.Tag = _qbq;
                    }
                }
    
                DataClickerQueries _queries = this.Parent.SQLLiteApp.GetQueries("AND FOLDER = '" + parentFolder.Guid + "'");
                foreach (DataClickerQuery _qbq in _queries.Items)
                {
                    TreeNode _node = parentNode.Nodes.Add("", _qbq.Name, "Query", "QuerySelected");
                    _node.Tag = _qbq;
                }
            }

    i tried this:

    private void LoadTreeViewFoldersChilds(TreeNode parentNode)
            {
                DataClickerFolder parentFolder = (DataClickerFolder)parentNode.Tag;
                DataClickerFolders _folders = this.Parent.SQLLiteApp.GetFolders("WHERE PARENT = '" + parentFolder.Guid + "'");
    
                Parallel.ForEach(_folders.Items, (DataClickerFolder _qbq) =>
                {
                    if (_qbq.FolderType == FolderTypes.Folder)
                    {
                        TreeNode _node = parentNode.Nodes.Add("", _qbq.Name, "Folder", "FolderSelected");
                        _node.Tag = _qbq;
                        // Read child folders 
                        LoadTreeViewFoldersChilds(_node);
                    }
                    else
                    {
                        TreeNode _node = parentNode.Nodes.Add("", _qbq.Name, "Query", "QuerySelected");
                        _node.Tag = _qbq;
                    }
                });
    
                DataClickerQueries _queries = this.Parent.SQLLiteApp.GetQueries("AND FOLDER = '" + parentFolder.Guid + "'");
                foreach (DataClickerQuery _qbq in _queries.Items)
                {
                    TreeNode _node = parentNode.Nodes.Add("", _qbq.Name, "Query", "QuerySelected");
                    _node.Tag = _qbq;
                }
            }	

    This is not the right way i get a warning:

    System.InvalidOperationException:
    "The action performed for this control is invoked from the wrong thread.
    Guide the action with Control.Invoke or Control.BeginInvoke
    to the correct thread to execute it. '

    Saturday, June 29, 2019 5:32 PM

Answers

  • The problem that you are seeing occurs any time that you modify a form control in a desktop application from a thread that is not the same one that created the form. This not only happens when you create the thread by means of a Parallel.Foreach. If you were to create the thread in any other way, you would still experience the same issue. Basically, it is not allowed. You cannot manipulate the screen from a different thread.

    So, how do you do it? You use your thread to do any lengthy calculations you need and you put the results into variables (and not directly onto controls that are visible on screen such as a TreeView). Once the calculations are done (i.e., when you exit the Parallel.Foreach) then you take the variables and transfer the values to the controls. -OR- if you absolutely have to paint things on screen from inside the thread, then you use the Invoke method of a control to transfer execution to the main thread to present the value. But be warned that this "freezes" the thread until the main thread can respond, so depending on when you do it, it can prevent the multithreading code from actually running in parallel.

    • Marked as answer by HansvB69 Sunday, June 30, 2019 6:08 PM
    Saturday, June 29, 2019 8:08 PM
    Moderator