如何修复C# MongoDB自动补全输入过快导致的崩溃问题
解决WinForms输入过快导致MongoDB查询崩溃的问题
问题根源
你的代码在TextChanged事件中同步执行MongoDB查询,会阻塞UI线程;输入过快时会触发大量连续的查询任务,不仅导致UI卡顿,还可能因线程冲突或内存访问不当引发System.AccessViolationException。另外,若后续改为异步但未处理线程问题,直接操作控件也会引发跨线程错误。
修复方案
通过异步查询+取消令牌+UI线程安全更新解决,核心逻辑是:取消上一次未完成的查询,异步执行新查询,仅在查询完成且未被取消时更新UI控件。
步骤1:添加字段声明
在窗体类中添加CancellationTokenSource字段,用于管理查询任务的取消:
private CancellationTokenSource _previousCts;
步骤2:重构TextChanged事件处理方法
将同步查询改为异步,加入取消逻辑,并确保UI操作在UI线程执行:
private async void textBox2_TextChanged(object sender, EventArgs e) { string searchTerm = textBoxStudNum.Text.Trim(); // 取消上一次未完成的查询并释放资源 _previousCts?.Cancel(); _previousCts?.Dispose(); _previousCts = new CancellationTokenSource(); var currentCts = _previousCts; if (!string.IsNullOrWhiteSpace(searchTerm)) { try { var filter = Builders<StudentRecord>.Filter.Regex(s => s.studentNumber, new BsonRegularExpression(searchTerm, "i")); var projection = Builders<StudentRecord>.Projection .Include(s => s.studentNumber) .Include(s => s.firstName) .Include(s => s.lastName) .Include(s => s.middleName) .Include(s => s.college) .Include(s => s.year); // 异步执行MongoDB查询,传入取消令牌 var studentRecords = await _studentRecordCollection .Find(filter) .Project<StudentRecord>(projection) .Limit(10) .ToListAsync(currentCts.Token); // 检查当前任务是否未被取消,避免更新过时结果 if (!currentCts.IsCancellationRequested) { // 确保在UI线程更新控件 textBoxStudNum.Invoke(new Action(() => UpdateAutoComplete(studentRecords))); } } catch (OperationCanceledException) { // 任务被取消,无需处理 } catch (Exception ex) { MessageBox.Show($"查询出错:{ex.Message}"); } } else { // 清空补全建议,确保在UI线程操作 textBoxStudNum.Invoke(new Action(() => { _suggestedStudentNumbers.Clear(); textBoxStudNum.AutoCompleteCustomSource = null; })); } } // 提取更新AutoComplete的逻辑,复用代码 private void UpdateAutoComplete(List<StudentRecord> studentRecords) { _suggestedStudentNumbers = studentRecords.Select(s => s.studentNumber).ToList(); var autoComplete = new AutoCompleteStringCollection(); autoComplete.AddRange(_suggestedStudentNumbers.ToArray()); textBoxStudNum.AutoCompleteCustomSource = autoComplete; textBoxStudNum.AutoCompleteMode = AutoCompleteMode.Suggest; textBoxStudNum.AutoCompleteSource = AutoCompleteSource.CustomSource; }
关键优化点说明
- 取消令牌管理:每次触发输入事件时,先取消上一次未完成的查询,避免任务堆积,同时释放旧资源。
- 异步查询:使用MongoDB驱动的
ToListAsync()方法,不阻塞UI线程,保证输入操作流畅。 - UI线程安全:通过
Invoke确保控件操作在UI线程执行,避免跨线程访问异常。 - 取消检查:查询完成后验证任务状态,避免更新已过时的查询结果(比如用户已输入新内容,旧结果无意义)。
之前用取消令牌导致补全无显示的原因
大概率是取消任务后未正确处理后续有效查询,或未在UI线程更新控件。上述方案中,每次输入都会创建新的令牌对象,仅取消上一次任务,同时确保有效查询完成后能在UI线程更新补全列表,不会出现补全消失的问题。
内容的提问来源于stack exchange,提问作者Jeffrey Pascual
相关产品推荐
相关产品推荐

