Winforms 后台工作者卡住了

Winforms background worker gets stuck

我正在尝试构建一个简单的代码,将 csv 文件连接到一个不同的文件中,但我的后台工作人员似乎有自己的想法,我的代码每次都卡住。 这是我使用后台工作者加入文件的代码:

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {
            if (string.IsNullOrEmpty(saveFilePath))
            {
                this.Invoke(new MethodInvoker(delegate
                {
                    btnBrowseSave.PerformClick();
                }));
            }
            if (!string.IsNullOrEmpty(saveFilePath))
            {
                if (dragEventArgs != null)
                    files = (string[])dragEventArgs.Data.GetData(DataFormats.FileDrop);

                int filesCount = 0, rowsCount = 0;
                foreach (string file in files)
                {
                    filesCount++;
                    int fileTotalLines = File.ReadAllLines(file).Length;

                    this.Invoke(new MethodInvoker(delegate
                    {
                        lblFileName.Text = "Loading file: " + file.Substring(file.LastIndexOf("\") + 1);
                        lblTotalFiles.Text = "File " + filesCount + " of " + files.Length;
                    }));

                    using (StreamReader reader = new StreamReader(file))
                    {
                        using (StreamWriter writer = new StreamWriter(saveFilePath))
                        {
                            while (!reader.EndOfStream)
                            {
                                try
                                {
                                    while (stopPosition > rowsCount)
                                    {
                                        reader.ReadLine();
                                        rowsCount++;
                                    }
                                    string email = reader.ReadLine().Trim();
                                    if (!string.IsNullOrEmpty(email) && !dicEmails.ContainsKey(email))
                                    {
                                        dicEmails.Add(email, null);
                                        writer.WriteLine(email);
                                    }
                                    rowsCount++;
                                    stopPosition++;

                                    backgroundWorker.ReportProgress((rowsCount * 100 / fileTotalLines), (int)ProgressType.Row);
                                    if (backgroundWorker.CancellationPending)
                                        return;
                                }
                                catch (Exception ex)
                                {
                                    hadReadErrors = true;
                                }
                            }
                        }
                    }
                    backgroundWorker.ReportProgress(0, (int)ProgressType.Row);
                    backgroundWorker.ReportProgress((filesCount * 100 / files.Length), (int)ProgressType.File);
                }
            }
        }
        catch (Exception ex)
        {
            hadReadErrors = true;
            MessageBox.Show(ex.Message);
        }
        finally
        {
            backgroundWorker.Dispose();
        }
    }

    private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        try
        {
            switch ((int)e.UserState)
            {
                case (int)ProgressType.Row:
                    lblFileProgress.Text = e.ProgressPercentage + "%";
                    fileProgressBar.Value = e.ProgressPercentage;
                    break;
                case (int)ProgressType.File:
                    lblTotalProgress.Text = e.ProgressPercentage + "%";
                    totalProgressBar.Value = e.ProgressPercentage;
                    break;
            }
        }
        catch (Exception ex) { }
    }

当我 运行 处于调试模式并使用调试器时,我没有发现任何问题,但是当我让代码 运行 自行运行时,它会卡住并崩溃。 有人可以帮助我并告诉我我在这里遗漏了什么吗?

例外情况:

Managed Debugging Assistant 'ContextSwitchDeadlock' has detected a problem in    
'C:\Users\Develop\Desktop\ExcelBuilder\ExcelBuilder\bin\Debug\ExcelBuilder.vshost.exe'.

Additional information: The CLR has been unable to transition from COM context 0x90fb78 
to COM context 0x90fc30 for 60 seconds. The thread that owns the destination
context/apartment is most likely either doing a non pumping wait or processing a very 
long running operation without pumping Windows messages. This situation generally has 
a negative performance impact and may even lead to the application becoming non 
responsive or memory usage accumulating continually over time. To avoid this problem, 
all single threaded apartment (STA) threads should use pumping wait primitives 
(such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.

我做了一个你程序的小例子,试图猜测它必须做什么(https://github.com/anderson-rancan/Whosebug_28798348,拖放 4 个文件到组框,lorem?.csv),有几件事你应该考虑:

所以,只要修复它就可以 运行 它,就像这样:

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker bckw = (BackgroundWorker)sender; // Recommended way, thread safe

    try
    {
        if (string.IsNullOrEmpty(saveFilePath))
        {
            this.Invoke(new MethodInvoker(delegate
            {
                btnBrowseSave.PerformClick();
            }));
        }
        if (!string.IsNullOrEmpty(saveFilePath))
        {
            if (dragEventArgs != null)
                files = (string[])dragEventArgs.Data.GetData(DataFormats.FileDrop);

            int filesCount = 0, rowsCount = 0;
            foreach (string file in files)
            {
                filesCount++;
                double fileTotalLines = File.ReadAllLines(file).Length;

                this.BeginInvoke(new MethodInvoker(delegate
                {
                    lblFileName.Text = "Loading file: " + file.Substring(file.LastIndexOf("\") + 1);
                    lblTotalFiles.Text = "File " + filesCount + " of " + files.Length;
                })); // Invoke async, way too fast this...

                using (StreamReader reader = new StreamReader(file))
                {
                    using (StreamWriter writer = new StreamWriter(saveFilePath))
                    {
                        while (!reader.EndOfStream)
                        {
                            try
                            {
                                while (stopPosition > rowsCount)
                                {
                                    reader.ReadLine();
                                    rowsCount++;
                                } // why are you using that? it won't get TRUE

                                string email = reader.ReadLine().Trim();
                                if (!string.IsNullOrEmpty(email) && !dicEmails.ContainsKey(email))
                                {
                                    dicEmails.Add(email, null);
                                    writer.WriteLine(email);
                                }
                                rowsCount++;
                                stopPosition++;

                                bckw.ReportProgress((int)Math.Round(rowsCount * 100 / fileTotalLines, 0), (int)ProgressType.Row);
                                if (bckw.CancellationPending)
                                    return;
                            }
                            catch (Exception ex)
                            {
                                hadReadErrors = true;
                                throw; // Throw it again, or you won't know the Exception
                            }
                        }
                    }
                }
                bckw.ReportProgress(0, (int)ProgressType.Row);
                bckw.ReportProgress((filesCount * 100 / files.Length), (int)ProgressType.File);
            }
        }
    }
    catch (Exception ex)
    {
        hadReadErrors = true;
        MessageBox.Show(ex.Message);
    }
    finally
    {
        bckw.Dispose();
    }
}

private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    //try
    //{
    switch ((int)e.UserState)
    {
        case (int)ProgressType.Row:
            lblFileProgress.Text = e.ProgressPercentage + "%";
            if (e.ProgressPercentage <= fileProgressBar.Maximum)
                fileProgressBar.Value = e.ProgressPercentage;
            break;
        case (int)ProgressType.File:
            lblTotalProgress.Text = e.ProgressPercentage + "%";
            totalProgressBar.Value = e.ProgressPercentage;
            break;
    }
    //}
    //catch (Exception ex) { } // Don't catch everything
}

最后,我可以建议另一种方法吗? 您正在读取文件两次:一次获取行数,另一次读取每一行。尝试只做一次,你会得到更好的结果。