后台工作者锁定主线程 - Windows窗体C#

时间:2014-02-28 14:23:10

标签: c# multithreading winforms user-interface backgroundworker

我有一个后台工作程序,用于在后台创建文件。 我让它工作,以便创建文件,UI仍然响应。 我做了一些更改,现在我无法弄清楚为什么后台工作者正在锁定我的主线程。

以下是我的后台工作方法。我没有改变进度的事件。

private void filecreator_bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        if (filecreator_bgw.CancellationPending == true)
        {
            e.Cancel = true;
        }
        else
        {
            myManager.createFiles((SelectedFileTypes) e.Argument);
        }
    }

private void filecreator_bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled == true)
            {
                //status_label.Text = "Canceled!";
            }
            else if (e.Error != null)
            {
                //status_label.Text = "Error: " + e.Error.Message;
            }
            else
            {
                // Check the file manager object to see if the files were created successfully
                    status_label.Text = "COMPLETE";
                    file_statusLabel.Text = "Files Created: " + DateTime.Now.ToShortTimeString();
                    System.Threading.Thread.Sleep(5000);
                    status_label.Text = "Click Create Files to Begin";
                    createfiles_button.Enabled = true;
            }
        }

以下是创建文件的方法。

public void createFiles(SelectedFileTypes x)
        {
            if (string.IsNullOrEmpty(Filename) || (x.isCSV == false && x.isTAB == false && x.isXML == false))
            {
                filesCreated = false;
                return;
            }

            // Declare the streams and xml objects used to write to the output files
            XDocument xmlFile;
            StreamWriter swCSV;
            StreamWriter swTAB;

            CSVFilename = Path.GetDirectoryName(Filename) + Path.DirectorySeparatorChar.ToString() +
                Path.GetFileNameWithoutExtension(Filename) + "CSV_TEST.csv";
            swCSV = new StreamWriter(CSVFilename);

            TABFilename = Path.GetDirectoryName(Filename) + Path.DirectorySeparatorChar.ToString() +
                Path.GetFileNameWithoutExtension(Filename) + "TAB_TEST.csv";
            swTAB = new StreamWriter(TABFilename);

            XMLFilename = Path.GetDirectoryName(Filename) + Path.DirectorySeparatorChar.ToString() +
                Path.GetFileNameWithoutExtension(Filename) + "XML_TEST.csv";
            xmlFile = new XDocument(
                        new XDeclaration("1.0", "utf-8", "yes"),
                        new XComment("Crosswalk"));
            xmlFile.Add(new XElement("ACCOUNTS"));

            // String array for use when creating xml nodes
            string[] splits;

            // String used to read in a line from the input file
            string line = "";

            // Use a try and catch block, if any errors are caught, return false
            try
            {
                // Read each line in the file and write to the output files
                using (StreamReader sr = new StreamReader(Filename))
                {
                    int i = 0;
                    while ((line = sr.ReadLine()) != null)
                    {
                        if (x.isCSV)
                        {
                            swCSV.WriteLine(line.Replace(delim, ","));
                        }
                        if (x.isTAB)
                        {
                            swTAB.WriteLine(line.Replace(delim, "\t"));
                        }
                        if (x.isXML)
                        {
                            if (i <= 0)
                            {
                                i++;
                                continue;
                            }

                            splits = line.Split(new string[] { delim }, StringSplitOptions.RemoveEmptyEntries);
                            xmlFile.Root.Add(
                                new XElement("ACCOUNTS",
                                    from s in header
                                    select new XElement(s, splits[Array.IndexOf(header, header.Where(z => z.Equals(s, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault())])
                                    )
                                );
                        }
                    }

                    // Dispose of all objects
                        swCSV.Close();
                        swCSV.Dispose();
                        swTAB.Close();
                        swTAB.Dispose();
                    if (x.isXML)
                    {
                        //xmlFile.Save(Path.GetFullPath(Filename) + Path.GetFileNameWithoutExtension(Filename) + "_TEST.xml");
                        xmlFile.Save(XMLFilename);
                    }
                }
            }
            catch (Exception)
            {
                filesCreated = false;
                return;
            }

            // Return true if file creation was successfull
            filesCreated = true;
        }

在do work方法中,我构建了一个简单的结构来确定应该生成哪些输出文件类型,然后将其传递给方法。如果我注释掉那个创建文件的调用,那么UI仍然没有响应。

在create files方法中,我根据我正在转换的输入文件构建文件。我使用LINQ语句来帮助构建XML标记,但是包含标记值的数组很小,3-5个元素取决于所选择的文件。

是否有简单的解决方案,或者我应该重新设计该方法。如果我必须重新设计,我应该记住什么以避免锁定主线程。 感谢

以下是我如何调用runworkerasync方法:

    private void createfiles_button_Click(object sender, EventArgs e)
    {
        SelectedFileTypes selVal = new SelectedFileTypes();
        foreach (var structVal in outputformats_checkedListBox.CheckedItems)
        {
            if (structVal.ToString().Equals("CSV", StringComparison.InvariantCultureIgnoreCase))
                selVal.isCSV = true;
            if (structVal.ToString().Equals("TAB", StringComparison.InvariantCultureIgnoreCase))
                selVal.isTAB = true;
            if (structVal.ToString().Equals("XML", StringComparison.InvariantCultureIgnoreCase))
                selVal.isXML = true;
        }

        // Call the FileManager object's create files method
        createfiles_button.Enabled = false;
        filecreator_bgw.RunWorkerAsync(selVal);
    }

更新: 我更新了调用以启动worker,然后调用使用传递给worker的参数创建文件。

1 个答案:

答案 0 :(得分:3)

您无法直接从BackgroundWorker与大多数UI控件进行交互。您需要从UI线程访问outputformats_checkedListBox.CheckedItems,并将生成的SelectedFileTypes对象作为参数传递到BackgroundWorker

此外,请求注意您的取消逻辑确实没有做太多。为了使其运作良好,您需要在整个过程中检查CancellationPending,而不仅仅是在启动时。

这是一个如何启动工人的粗略示例:

private void StartWorker()
{
    SelectedFileTypes selVal = new SelectedFileTypes();
    foreach (var structVal in outputformats_checkedListBox.CheckedItems)
    {
        if (structVal.ToString().Equals("CSV", StringComparison.InvariantCultureIgnoreCase))
            selVal.isCSV = true;
        if (structVal.ToString().Equals("TAB", StringComparison.InvariantCultureIgnoreCase))
                selVal.isTAB = true;
        if (structVal.ToString().Equals("XML", StringComparison.InvariantCultureIgnoreCase))
            selVal.isXML = true;
    }
    filecreator_bgw.RunWorkerAsync(selVal);
}

private void filecreator_bgw_DoWork(object sender, DoWorkEventArgs e)
{
    SelectedFileTypes selVal = (SelectedFileTypes)e.Argument;
    myManager.createFiles(selVal);
}