You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中BackgroundWorker多线程文本搜索ListView显示异常求助

WinForm多线程文本搜索问题排查(BackgroundWorker)

嘿,我看了你用BackgroundWorker实现文本搜索的代码,确实踩了两个典型的新手坑——行号始终为0和内容重复显示,咱们一步步来拆解问题:

问题1:行号永远是0的根源

你的linenumbercount方法逻辑完全走偏了:

  • 你把行号作为参数传入,但调用后没有把返回值更新回类的linenumber1字段,导致每次调用都传初始值0,自然永远返回0开头的数字。
  • 行号的逻辑错了:行号是每读一行就递增,不管这行有没有匹配关键词,而不是只有匹配时才处理。现在的写法完全没跟上文件的行号进度。

问题2:内容重复显示的原因

  • 你用类字段line2存储匹配到的行内容,一旦某行匹配成功,line2就会保留这个值;如果下一行不匹配,CountInString会直接返回之前的line2,导致不匹配的行也带着旧内容上报给UI。
  • 你不管行是否匹配,每隔一秒就上报一次进度,这会把不匹配的行(带着旧内容)也塞进ListView里,自然重复。

直接上修正后的代码吧,我把逻辑理清楚了:

首先是main_work类的核心逻辑修正

class main_work
{
    public class ReportState
    {
        // 改个清晰的命名,避免混淆
        public int LineNumber;
        public string LineContent;
    }

    public string SourceFile;
    public string CompareString;

    public void CountWords(System.ComponentModel.BackgroundWorker worker, System.ComponentModel.DoWorkEventArgs e)
    {
        // 这里不能直接弹MessageBox,跨线程会报错,改成抛异常在UI层处理
        if (string.IsNullOrEmpty(CompareString))
        {
            throw new InvalidOperationException("Please Enter a string to be searched");
        }

        int currentLineNumber = 0;
        DateTime lastReportTime = DateTime.Now;
        // 控制上报频率,避免UI卡顿
        int reportInterval = 100; // 毫秒

        using (var reader = new System.IO.StreamReader(SourceFile))
        {
            while (!reader.EndOfStream)
            {
                // 先检查是否要取消任务
                if (worker.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }

                currentLineNumber++;
                string currentLine = reader.ReadLine();

                // 只有匹配到关键词时才上报UI
                if (currentLine?.Contains(CompareString) == true)
                {
                    // 控制上报频率,避免UI频繁刷新
                    if (DateTime.Now.Subtract(lastReportTime).TotalMilliseconds >= reportInterval)
                    {
                        var state = new ReportState
                        {
                            LineNumber = currentLineNumber,
                            LineContent = currentLine
                        };
                        worker.ReportProgress(0, state);
                        lastReportTime = DateTime.Now;
                    }
                    else
                    {
                        // 如果不需要频率控制,直接上报就行
                        var state = new ReportState
                        {
                            LineNumber = currentLineNumber,
                            LineContent = currentLine
                        };
                        worker.ReportProgress(0, state);
                    }
                }

                // 稍微睡一下,别占满CPU
                System.Threading.Thread.Sleep(1);
            }
        }
    }
}

然后是Form里的ProgressChanged事件修正

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    var state = (main_work.ReportState)e.UserState;
    // 虽然BackgroundWorker的ProgressChanged在UI线程,但加个Invoke检查更稳妥
    if (listView1.InvokeRequired)
    {
        listView1.Invoke(new Action(() => AddSearchResult(state)));
    }
    else
    {
        AddSearchResult(state);
    }
}

// 把添加ListView项的逻辑抽出来,更清晰
private void AddSearchResult(main_work.ReportState state)
{
    var item = new ListViewItem(state.LineNumber.ToString());
    item.SubItems.Add(state.LineContent);
    listView1.Items.Add(item);
}

最后补个RunWorkerCompleted里的异常处理修正

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error != null)
    {
        MessageBox.Show("Error: " + e.Error.Message);
    }
    else if (e.Cancelled)
    {
        MessageBox.Show("Search canceled.");
    }
    else
    {
        MessageBox.Show("Search finished.");
    }
}

几个额外的小提醒:

  1. 绝对不要在DoWork里操作UI:之前你在CountWords里弹MessageBox,这会直接触发跨线程异常,一定要通过RunWorkerCompleted或者ProgressChanged来处理UI操作。
  2. 命名规范很重要:把模糊的变量名(比如line2、LinesCounted1)改成有意义的名字,后续维护起来会轻松很多。
  3. 取消操作要及时:在循环里检查CancellationPending后要立刻返回,确保线程能快速终止。

内容的提问来源于stack exchange,提问作者user6187871

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:24:41