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

如何让System.Windows.Forms.Timer调用的方法等待另一个Timer方法执行完成?

让System.Windows.Forms.Timer方法等待System.Timers.Timer方法完成的解决方案

当然可以实现这个需求!不过得先注意两个计时器的线程差异:System.Timers.Timer的事件默认在ThreadPool后台线程执行,而System.Windows.Forms.Timer的Tick事件始终在UI线程执行。直接阻塞等待很容易导致UI假死,所以得用异步同步的方式来处理。

先看看你现有代码的问题:目前你是交替启停两个计时器,但如果fileRead()耗时超过1.5秒,aTimer的Elapsed事件可能在fileRead()还没完成时就触发,根本没做到“等待前者完成”的效果。而且两个计时器独立计时的逻辑,本来就容易出现不同步的情况。

下面给你两种可行的实现方案:


方案一:用TaskCompletionSource跟踪执行状态(推荐)

这个方案用TaskCompletionSource来标记fileRead()的完成状态,让prepareData()异步等待它完成,同时避免阻塞UI线程。

步骤1:添加全局变量和初始化计时器

先定义用于同步的变量,并把计时器设为单次触发(避免重复触发):

private TaskCompletionSource<bool> _fileReadCompletion;
private readonly TimeSpan _interval = TimeSpan.FromSeconds(1.5);
private System.Timers.Timer aTimer;
private System.Windows.Forms.Timer myTimer;
private readonly object _graphLock = new object(); // 保证graphList线程安全

// 初始化计时器的代码
private void InitTimers()
{
    // 配置System.Timers.Timer
    aTimer = new System.Timers.Timer(_interval.TotalMilliseconds);
    aTimer.AutoReset = false; // 单次触发,避免重复执行
    aTimer.Elapsed += prepData;

    // 配置WinForms Timer
    myTimer = new System.Windows.Forms.Timer();
    myTimer.Interval = (int)_interval.TotalMilliseconds;
    myTimer.Tick += FileReadFunction;
}

步骤2:修改FileReadFunction方法

把fileRead()放到后台线程执行,完成后标记状态并启动下一个计时器:

private async void FileReadFunction(object sender, EventArgs e)
{
    myTimer.Stop();
    _fileReadCompletion = new TaskCompletionSource<bool>();

    try
    {
        // 后台执行fileRead,不阻塞UI
        await Task.Run(() =>
        {
            lock (_graphLock) // 线程安全访问graphList
            {
                foreach (NetPinger.source.AddGraph b in graphList)
                {
                    b.fileRead();
                }
            }
        });
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
    finally
    {
        _fileReadCompletion.SetResult(true); // 标记fileRead完成
        aTimer.Start(); // 启动aTimer执行prepareData
    }
}

步骤3:修改prepData方法

异步等待fileRead()完成后再执行,最后延迟指定间隔重启WinForms Timer:

public async void prepData(object objectInfo, EventArgs e)
{
    aTimer.Stop();

    // 等待fileRead执行完成
    if (_fileReadCompletion != null)
    {
        await _fileReadCompletion.Task;
    }

    try
    {
        lock (_graphLock) // 线程安全访问graphList
        {
            foreach (NetPinger.source.AddGraph b in graphList)
            {
                b.prepareData();
            }
        }
    }
    finally
    {
        // 等待1.5秒后重启myTimer,保持原间隔逻辑
        await Task.Delay(_interval);
        myTimer.Start();
    }
}

方案二:用ManualResetEventSlim同步(适合熟悉传统同步机制的场景)

如果你更习惯用传统的同步信号量,可以用ManualResetEventSlim,但同样要结合异步等待避免阻塞UI:

private ManualResetEventSlim _fileReadDone = new ManualResetEventSlim(false);

// 修改FileReadFunction
private async void FileReadFunction(object sender, EventArgs e)
{
    myTimer.Stop();
    _fileReadDone.Reset();

    try
    {
        await Task.Run(() =>
        {
            lock (_graphLock)
            {
                foreach (NetPinger.source.AddGraph b in graphList)
                {
                    b.fileRead();
                }
            }
            _fileReadDone.Set(); // 发出完成信号
        });
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
        _fileReadDone.Set(); // 异常时也要释放信号,避免死等
    }
    finally
    {
        aTimer.Start();
    }
}

// 修改prepData
public async void prepData(object objectInfo, EventArgs e)
{
    aTimer.Stop();

    // 异步等待信号量,不阻塞线程
    await Task.Run(() => _fileReadDone.Wait());

    try
    {
        lock (_graphLock)
        {
            foreach (NetPinger.source.AddGraph b in graphList)
            {
                b.prepareData();
            }
        }
    }
    finally
    {
        await Task.Delay(_interval);
        myTimer.Start();
    }
}

关键注意点

  1. 线程安全:因为fileRead()在后台线程,prepareData()在UI线程,两者都访问graphList,一定要加锁避免并发问题。
  2. 避免UI阻塞:所有耗时操作都放到Task.Run()里,用await异步等待,绝不能在UI线程调用Wait()或Thread.Sleep()。
  3. 计时器配置:把System.Timers.Timer的AutoReset设为false,确保每次启动只触发一次事件,避免重复执行。

内容的提问来源于stack exchange,提问作者C.User

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 09:07:35