进一步优化上一版本的异步管道和任务取消机制,提供更简洁的代码,使用生产者-消费者模式和并行任务调度,结合 Task 和 async/await,确保线程安全、高效且现代化
此外,你要求进一步优化上一版本的异步管道和任务取消机制,提供更简洁的代码,使用生产者-消费者模式和并行任务调度,结合 Task 和 async/await,确保线程安全、高效且现代化。完整优化代码以下代码基于你的场景(WinForms 应用定期更新 UI 显示 sectionS 测试结果),使用生产者-消费者模式,优化异步管道和取消机制。以下是优化后的完整代码示例,修复上述问题,包含简洁注释、详细
·
感谢你的反馈!从你的描述来看,存在两个问题需要解决:
- “Task Form1.button1_Click(object, EventArgs)”的返回类型错误:
- button1_Click 是一个 WinForms 事件处理程序,通常返回 void,而不是 Task。如果声明为 async Task,会导致编译错误,因为 WinForms 事件处理程序的签名必须是 void (object, EventArgs)。
- 解决方法:将 button1_Click 改为 async void,并在其中调用异步方法,确保符合事件处理程序签名。
- “未提供与‘Form1.Form1(ILogger)’的所需参数‘logger’对应的参数”:
- 构造函数 YourForm(ILogger logger) 需要一个 ILogger 参数,但在实例化时未提供。
- 解决方法:在创建 YourForm 实例时,传入一个 ILogger 实现(如模拟日志或实际日志框架实例)。
此外,你要求进一步优化上一版本的异步管道和任务取消机制,提供更简洁的代码,使用生产者-消费者模式和并行任务调度,结合 Task 和 async/await,确保线程安全、高效且现代化。
以下是优化后的完整代码示例,修复上述问题,包含简洁注释、详细解释和测试用例。
优化目标
- 修复事件处理程序:将 button1_Click 改为 async void,符合 WinForms 签名。
- 修复构造函数:提供 ILogger 实现,解决参数缺失。
- 异步管道优化:
- 仅入队变化的状态,减少队列操作。
- 批量处理 UI 更新,降低线程切换。
- 使用 ConfigureAwait(false) 优化异步性能。
- 任务取消机制:通过 CancellationToken 优雅终止任务。
- 并行任务调度:使用 Parallel.ForEach 加速状态更新。
- 线程安全:使用 ConcurrentDictionary 和 ConcurrentQueue。
- 简洁性:精简代码和注释,保持清晰。
完整优化代码以下代码基于你的场景(WinForms 应用定期更新 UI 显示 sectionS 测试结果),使用生产者-消费者模式,优化异步管道和取消机制。
using System;
using System.Collections.Concurrent;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
// 模拟日志接口,实际使用 NLog 或 Serilog
public interface ILogger
{
void Info(string message);
void Error(string message);
}
// 模拟日志实现
public class ConsoleLogger : ILogger
{
public void Info(string message) => Console.WriteLine($"INFO: {message}");
public void Error(string message) => Console.WriteLine($"ERROR: {message}");
}
public partial class YourForm : Form
{
private readonly ConcurrentDictionary<string, string> sectionS = new(); // 测试状态
private readonly ConcurrentQueue<(string Key, string Value)> statusQueue = new(); // 状态队列
private readonly Dictionary<string, Label> labelCache = new(); // Label 缓存
private static readonly Font LabelFont = new("上首漠云体", 25); // 字体缓存
private volatile bool m_Dis; // 退出标志
private int _LX = 10, _LY = 25, _YSpace = 10; // UI 布局
private DateTime now = DateTime.Now; // 时间基准
private readonly ILogger m_Logger; // 日志
private CancellationTokenSource cts; // 取消令牌
public YourForm(ILogger logger)
{
m_Logger = logger ?? throw new ArgumentNullException(nameof(logger));
InitializeComponent();
}
// 按钮点击事件,启动或取消任务
private async void button1_Click(object sender, EventArgs e)
{
if (cts == null || cts.IsCancellationRequested)
{
cts = new CancellationTokenSource();
m_Logger.Info("Starting task via button click");
await StartTestResultAsync(cts.Token); // 启动任务
}
else
{
cts.Cancel(); // 取消任务
cts.Dispose();
cts = null;
m_Logger.Info("Task cancelled via button click");
}
}
// 启动异步管道
public async Task StartTestResultAsync(CancellationToken ct = default)
{
try
{
m_Logger.Info("Starting pipeline");
await Task.WhenAll(ProduceStatusUpdatesAsync(ct), ConsumeStatusUpdatesAsync(ct));
m_Logger.Info("Pipeline completed");
}
catch (OperationCanceledException)
{
m_Logger.Info("Pipeline cancelled");
}
catch (Exception ex)
{
m_Logger.Error($"StartTestResultAsync failed: {ex.Message}");
throw;
}
}
// 生产者:更新状态并入队变化
private async Task ProduceStatusUpdatesAsync(CancellationToken ct)
{
try
{
var startTime = DateTime.Now;
while (!m_Dis && !ct.IsCancellationRequested && (DateTime.Now - startTime).TotalSeconds < 300)
{
var changes = UpdateSectionStatus();
foreach (var change in changes)
statusQueue.Enqueue(change);
await Task.Delay(3000, ct).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
m_Logger.Info("Producer cancelled");
throw;
}
catch (Exception ex)
{
m_Logger.Error($"Producer failed: {ex.Message}");
throw;
}
}
// 消费者:批量处理队列,更新 UI
private async Task ConsumeStatusUpdatesAsync(CancellationToken ct)
{
try
{
var startTime = DateTime.Now;
while (!m_Dis && !ct.IsCancellationRequested && (DateTime.Now - startTime).TotalSeconds < 300)
{
if (statusQueue.IsEmpty)
{
await Task.Delay(100, ct).ConfigureAwait(false);
continue;
}
var batch = new List<(string Key, string Value)>();
while (statusQueue.TryDequeue(out var status))
batch.Add(status);
if (batch.Any())
{
if (InvokeRequired)
await Task.Run(() => Invoke((MethodInvoker)(() => UpdateUIBatch(batch))), ct);
else
UpdateUIBatch(batch);
}
}
}
catch (OperationCanceledException)
{
m_Logger.Info("Consumer cancelled");
throw;
}
catch (Exception ex)
{
m_Logger.Error($"Consumer failed: {ex.Message}");
throw;
}
}
// 并行更新状态,返回变化
private List<(string Key, string Value)> UpdateSectionStatus()
{
var changes = new ConcurrentBag<(string Key, string Value)>();
try
{
Parallel.ForEach(sectionS.Keys, key =>
{
var newValue = sectionS.AddOrUpdate(key, key, (k, old) =>
{
if (old.Contains("PASS-OVER") || old.Contains("FAIL-OVER") ||
old.Contains("准备中") || old.Contains("下压中"))
return old;
if (DateTime.Now > now.AddSeconds(8) && DateTime.Now < now.AddSeconds(20))
return "NG模式FAIL";
if (DateTime.Now > now.AddSeconds(20) && DateTime.Now < now.AddSeconds(30))
return "测试中";
return "OK模式PASS";
});
if (newValue != sectionS[key])
changes.Add((key, newValue));
});
}
catch (Exception ex)
{
m_Logger.Error($"UpdateSectionStatus failed: {ex.Message}");
}
return changes.ToList();
}
// 批量更新 UI
private void UpdateUIBatch(List<(string Key, string Value)> batch)
{
try
{
_LY = 25;
foreach (var (key, value) in batch)
{
if (!labelCache.TryGetValue(key, out var label))
{
label = new Label
{
Tag = key,
AutoSize = true,
Font = LabelFont,
Location = new Point(_LX, _LY)
};
panel5.Controls.Add(label);
labelCache[key] = label;
}
label.Text = GetStatusString(key, value);
label.ForeColor = value.Contains("PASS") ? Color.Green :
value.Contains("FAIL") ? Color.Red : Color.White;
label.Location = new Point(_LX, _LY);
_LY += _YSpace + label.Size.Height;
}
foreach (var key in labelCache.Keys.Except(sectionS.Keys).ToList())
{
if (labelCache.TryGetValue(key, out var label))
{
panel5.Controls.Remove(label);
label.Dispose();
labelCache.Remove(key);
}
}
}
catch (Exception ex)
{
m_Logger.Error($"UpdateUIBatch failed: {ex.Message}");
}
}
// 生成状态字符串
private string GetStatusString(string key, string value)
{
var sb = new StringBuilder(key).Append(':');
if (value.Contains("PASS-OVER")) return sb.Append("点检结果PASS").ToString();
if (value.Contains("FAIL-OVER")) return sb.Append("点检结果FAIL").ToString();
if (value.Contains("准备中") || value.Contains("下压中")) return sb.Append(value).ToString();
return sb.Append(value.Contains("FAIL") ? value : value.Contains("PASS") ? value : "测试中").ToString();
}
// 测试用例
private async Task RunTestsAsync()
{
sectionS.TryAdd("Test1", "准备中");
sectionS.TryAdd("Test2", "测试中");
sectionS.TryAdd("Test3", "下压中");
try
{
m_Logger.Info("Starting test");
cts = new CancellationTokenSource();
var task = StartTestResultAsync(cts.Token);
await Task.Delay(10000, cts.Token); // 10 秒后取消
cts.Cancel();
await task;
}
catch (OperationCanceledException)
{
m_Logger.Info("Test cancelled");
}
catch (Exception ex)
{
m_Logger.Error($"Test failed: {ex.Message}");
}
finally
{
cts?.Dispose();
cts = null;
}
}
}
// 程序入口
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new YourForm(new ConsoleLogger())); // 提供 ILogger 实例
}
}
每项详细解释
1. 修复事件处理程序
- 问题:Task Form1.button1_Click(object, EventArgs) 返回类型错误。
- 代码:
private async void button1_Click(object sender, EventArgs e) { if (cts == null || cts.IsCancellationRequested) { cts = new CancellationTokenSource(); m_Logger.Info("Starting task via button click"); await StartTestResultAsync(cts.Token); } else { cts.Cancel(); cts.Dispose(); cts = null; m_Logger.Info("Task cancelled via button click"); } } - 实现:
- 使用 async void 符合 WinForms 事件处理程序签名。
- 按钮点击启动或取消任务,管理 CancellationTokenSource。
- 解释:
- async void 允许异步调用 StartTestResultAsync,避免阻塞 UI 线程。
- cts 跟踪任务状态,支持启动/取消切换。
- 优点:
- 修复返回类型错误,符合事件处理要求。
- 支持用户交互(启动或取消任务)。
2. 修复构造函数参数
- 问题:未提供 ILogger 参数,导致实例化失败。
- 代码:
public YourForm(ILogger logger) { m_Logger = logger ?? throw new ArgumentNullException(nameof(logger)); InitializeComponent(); } Application.Run(new YourForm(new ConsoleLogger())); // 提供 ILogger - 实现:
- 定义 ConsoleLogger 作为 ILogger 的简单实现。
- 在 Main 中传入 ConsoleLogger 实例。
- 解释:
- ConsoleLogger 模拟日志输出,实际可替换为 NLog 等。
- 构造函数检查 logger 非空,确保初始化安全。
- 优点:
- 解决参数缺失问题。
- 提供灵活的日志实现,易于扩展。
3. 异步管道优化
- 代码:
private async Task ProduceStatusUpdatesAsync(CancellationToken ct) { var startTime = DateTime.Now; while (!m_Dis && !ct.IsCancellationRequested && (DateTime.Now - startTime).TotalSeconds < 300) { var changes = UpdateSectionStatus(); foreach (var change in changes) statusQueue.Enqueue(change); await Task.Delay(3000, ct).ConfigureAwait(false); } }private async Task ConsumeStatusUpdatesAsync(CancellationToken ct) { var startTime = DateTime.Now; while (!m_Dis && !ct.IsCancellationRequested && (DateTime.Now - startTime).TotalSeconds < 300) { if (statusQueue.IsEmpty) { await Task.Delay(100, ct).ConfigureAwait(false); continue; } var batch = new List<(string Key, string Value)>(); while (statusQueue.TryDequeue(out var status)) batch.Add(status); if (batch.Any()) if (InvokeRequired) await Task.Run(() => Invoke((MethodInvoker)(() => UpdateUIBatch(batch))), ct); else UpdateUIBatch(batch); } } - 实现:
- 生产者:仅入队变化的状态(通过 UpdateSectionStatus 返回),减少队列操作。
- 消费者:批量出队所有状态,调用 UpdateUIBatch 一次性更新 UI。
- 优化:ConfigureAwait(false) 避免上下文切换,Task.Delay(100) 减少空轮询。
- 解释:
- 仅入队变化降低队列开销,批量更新减少 UI 线程调用。
- 100ms 轮询平衡响应性和 CPU 使用。
- 优点:
- 减少不必要的队列和 UI 操作,提高性能。
- 异步管道支持动态数据流,保持 UI 响应性。
4. 任务取消机制
- 代码:
private CancellationTokenSource cts; private async void button1_Click(object sender, EventArgs e) { if (cts == null || cts.IsCancellationRequested) { cts = new CancellationTokenSource(); await StartTestResultAsync(cts.Token); } else { cts.Cancel(); cts.Dispose(); cts = null; } } - 实现:
- CancellationTokenSource 存储在类级别,按钮点击触发取消。
- ct.IsCancellationRequested 检查取消状态,Task.Delay 抛出 OperationCanceledException。
- Dispose 释放 cts 资源。
- 解释:
- 用户点击按钮可启动或取消任务,cts 跟踪状态。
- 取消后清理资源,确保无泄漏。
- 优点:
- 优雅终止生产者和消费者任务。
- 支持用户交互(如取消按钮)。
- 资源管理安全。
5. 并行任务调度
- 代码:
private List<(string Key, string Value)> UpdateSectionStatus() { var changes = new ConcurrentBag<(string Key, string Value)>(); Parallel.ForEach(sectionS.Keys, key => { var newValue = sectionS.AddOrUpdate(key, key, (k, old) => { /* 状态逻辑 */ }); if (newValue != sectionS[key]) changes.Add((key, newValue)); }); return changes.ToList(); } - 实现:
- Parallel.ForEach 并行更新 sectionS,利用多核 CPU。
- ConcurrentBag 收集变化的状态,线程安全。
- 解释:
- 并行处理加速大数据量(如 sectionS 含数百项)。
- 仅返回变化的状态,优化队列入队。
- 优点:
- 提高状态更新性能。
- 减少不必要的数据传递。
6. UI 优化
- 代码:
private void UpdateUIBatch(List<(string Key, string Value)> batch) { _LY = 25; foreach (var (key, value) in batch) { if (!labelCache.TryGetValue(key, out var label)) { label = new Label { Tag = key, AutoSize = true, Font = LabelFont, Location = new Point(_LX, _LY) }; panel5.Controls.Add(label); labelCache[key] = label; } label.Text = GetStatusString(key, value); label.ForeColor = value.Contains("PASS") ? Color.Green : value.Contains("FAIL") ? Color.Red : Color.White; label.Location = new Point(_LX, _LY); _LY += _YSpace + label.Size.Height; } foreach (var key in labelCache.Keys.Except(sectionS.Keys).ToList()) { if (labelCache.TryGetValue(key, out var label)) { panel5.Controls.Remove(label); label.Dispose(); labelCache.Remove(key); } } } - 实现:
- 缓存 Label,仅更新 Text 和 ForeColor。
- 批量处理队列状态,减少 UI 调用。
- 清理无用 Label,释放资源。
- 解释:
- 缓存避免重复创建控件,批量更新降低线程切换。
- Dispose 防止 GDI 泄漏。
- 优点:
- 减少 UI 线程负担,防止卡顿。
- 保持 UI 与数据一致。
7. 线程安全
- 代码:
private readonly ConcurrentDictionary<string, string> sectionS = new(); private readonly ConcurrentQueue<(string Key, string Value)> statusQueue = new(); private volatile bool m_Dis; - 实现:
- ConcurrentDictionary 确保 sectionS 线程安全。
- ConcurrentQueue 提供无锁队列操作。
- volatile 保证 m_Dis 跨线程可见。
- 解释:
- 原子操作避免数据竞争。
- volatile 防止缓存导致死循环。
- 优点:
- 确保数据一致,适合多线程。
- 无锁设计提高性能。
8. 资源管理
- code:
private static readonly Font LabelFont = new("上首漠云体", 25); label.Dispose(); cts.Dispose(); - 实现:
- 静态 Font 缓存 GDI 资源。
- Dispose 释放 Label 和 CancellationTokenSource。
- 解释:
- 避免重复创建字体,释放控件和取消令牌。
- 优点:
- 防止 GDI 和内存泄漏,适合长时间运行。
测试示例
private async Task RunTestsAsync()
{
sectionS.TryAdd("Test1", "准备中");
sectionS.TryAdd("Test2", "测试中");
sectionS.TryAdd("Test3", "下压中");
try
{
m_Logger.Info("Starting test");
cts = new CancellationTokenSource();
var task = StartTestResultAsync(cts.Token);
await Task.Delay(10000, cts.Token); // 10 秒后取消
cts.Cancel();
await task;
}
catch (OperationCanceledException)
{
m_Logger.Info("Test cancelled");
}
catch (Exception ex)
{
m_Logger.Error($"Test failed: {ex.Message}");
}
finally
{
cts?.Dispose();
cts = null;
}
}
测试说明:
- 初始化:添加 3 条测试数据,模拟 sectionS。
- 运行:启动管道任务,验证状态更新和 UI 渲染。
- 取消:10 秒后取消,检查退出行为。
- 日志:记录启动、取消和异常,验证功能。
预期结果:
- UI 显示 Test1、Test2、Test3 的状态,动态更新(如 “测试中” 到 “OK模式PASS”)。
- 10 秒后任务取消,日志显示 “Pipeline cancelled”。
- 无卡顿、无泄漏,Label 正确管理。
优点总结
- 异步管道:仅入队变化,批量更新 UI,降低开销。
- 任务取消:CancellationToken 支持优雅终止,用户交互友好。
- 并行调度:Parallel.ForEach 加速状态更新。
- UI 优化:缓存 Label,减少卡顿。
- 线程安全:Concurrent 结构和 volatile 确保一致性。
- 资源管理:静态字体、显式释放,防止泄漏。
注意事项
- 初始化:确保 _LX, _YSpace, now 正确设置。
- 日志:替换 ConsoleLogger 为实际框架。
- 测试:验证大数据量(sectionS 含 1000 项)和高频更新。
- 监控:使用 Visual Studio 诊断工具检查性能。
- 队列管理:可限制 statusQueue 长度(如 1000 项)。
如需进一步优化(如调整批量大小、增加并行度),请提供更多细节(如 sectionS 规模、UI 刷新频率)!
更多推荐
所有评论(0)