感谢你的反馈!从你的描述来看,存在两个问题需要解决:

  1. “Task Form1.button1_Click(object, EventArgs)”的返回类型错误:
    • button1_Click 是一个 WinForms 事件处理程序,通常返回 void,而不是 Task。如果声明为 async Task,会导致编译错误,因为 WinForms 事件处理程序的签名必须是 void (object, EventArgs)。
    • 解决方法:将 button1_Click 改为 async void,并在其中调用异步方法,确保符合事件处理程序签名。
  2. “未提供与‘Form1.Form1(ILogger)’的所需参数‘logger’对应的参数”:
    • 构造函数 YourForm(ILogger logger) 需要一个 ILogger 参数,但在实例化时未提供。
    • 解决方法:在创建 YourForm 实例时,传入一个 ILogger 实现(如模拟日志或实际日志框架实例)。

此外,你要求进一步优化上一版本的异步管道和任务取消机制,提供更简洁的代码,使用生产者-消费者模式和并行任务调度,结合 Task 和 async/await,确保线程安全、高效且现代化。

以下是优化后的完整代码示例,修复上述问题,包含简洁注释、详细解释和测试用例。


优化目标

  1. 修复事件处理程序:将 button1_Click 改为 async void,符合 WinForms 签名。
  2. 修复构造函数:提供 ILogger 实现,解决参数缺失。
  3. 异步管道优化:
    • 仅入队变化的状态,减少队列操作。
    • 批量处理 UI 更新,降低线程切换。
    • 使用 ConfigureAwait(false) 优化异步性能。
  4. 任务取消机制:通过 CancellationToken 优雅终止任务。
  5. 并行任务调度:使用 Parallel.ForEach 加速状态更新。
  6. 线程安全:使用 ConcurrentDictionary 和 ConcurrentQueue。
  7. 简洁性:精简代码和注释,保持清晰。

完整优化代码以下代码基于你的场景(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 正确管理。

优点总结

  1. 异步管道:仅入队变化,批量更新 UI,降低开销。
  2. 任务取消:CancellationToken 支持优雅终止,用户交互友好。
  3. 并行调度:Parallel.ForEach 加速状态更新。
  4. UI 优化:缓存 Label,减少卡顿。
  5. 线程安全:Concurrent 结构和 volatile 确保一致性。
  6. 资源管理:静态字体、显式释放,防止泄漏。

注意事项

  • 初始化:确保 _LX, _YSpace, now 正确设置。
  • 日志:替换 ConsoleLogger 为实际框架。
  • 测试:验证大数据量(sectionS 含 1000 项)和高频更新。
  • 监控:使用 Visual Studio 诊断工具检查性能。
  • 队列管理:可限制 statusQueue 长度(如 1000 项)。

如需进一步优化(如调整批量大小、增加并行度),请提供更多细节(如 sectionS 规模、UI 刷新频率)!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐