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

Timer阻塞主线程自动停止问题求助(附代码及Threading.Timer尝试)

问题分析与解决方案

原代码核心问题

  1. 主线程阻塞:原System.Timers.Timer的SQL查询是同步执行,且后续UI操作未正确切换线程,导致主线程被阻塞;改用System.Threading.Timer后,用Dispatcher.Invoke<Task<...>>().Result强制等待异步任务,依然会阻塞线程。
  2. Timer意外停止:Timer回调中未捕获异常,一旦SQL查询或业务逻辑抛出未处理异常,Timer会直接终止;同时AutoReset=false的手动重启逻辑存在漏洞,若某次执行未完成重启操作,Timer就会停掉。
  3. UI访问不安全:在非UI线程直接访问Datagrid_Selected.Items等UI元素,会引发线程安全异常,间接导致Timer停止。

修正后的实现方案

1. 线程安全的Timer管理

用System.Threading.Timer配合异步回调,确保Timer状态管理线程安全,避免重复启动或意外停止:

private System.Threading.Timer _meetingTimer;
private bool _isTimerRunning;
private readonly object _timerLock = new object();
private const int _tickIntervalMs = 2000; // 恢复需求的2秒间隔

// 初始化Timer
public void InitMeetingTimer()
{
    _meetingTimer = new System.Threading.Timer(OnTimerCallback, null, Timeout.Infinite, Timeout.Infinite);
}

// 启动Timer(双重检查锁保证线程安全)
public void StartTimer()
{
    if (!_isTimerRunning)
    {
        lock (_timerLock)
        {
            if (!_isTimerRunning)
            {
                _isTimerRunning = true;
                _meetingTimer.Change(_tickIntervalMs, Timeout.Infinite);
            }
        }
    }
}

// 停止Timer
public void StopTimer()
{
    lock (_timerLock)
    {
        _isTimerRunning = false;
        _meetingTimer.Change(Timeout.Infinite, Timeout.Infinite);
    }
}

2. 异步回调与安全操作

将所有IO操作(SQL查询/更新)异步化,用Dispatcher.InvokeAsync安全访问UI元素,同时添加异常捕获防止Timer终止:

private async void OnTimerCallback(object state)
{
    try
    {
        // 异步获取管理员配置,避免阻塞线程
        var adminSettings = await GetAdminSettingAsync("Daily_Duration");
        if (adminSettings == null || adminSettings.Count == 0)
        {
            StopTimer();
            return;
        }

        // 安全获取UI选中的日期(必须在UI线程执行)
        DateTime selectedDate = default;
        await Application.Current.Dispatcher.InvokeAsync(() =>
        {
            if (Datagrid_Selected?.Items?.Count > 0)
            {
                selectedDate = ((Argo_Snowflake)Datagrid_Selected.Items[0]).Date_Created;
            }
        });

        if (selectedDate == default)
        {
            _meetingTimer.Change(_tickIntervalMs, Timeout.Infinite);
            return;
        }

        // 异步获取会议数据
        var dailyMeetings = await GetDailyMeetingAsync("", selectedDate);

        // 处理会议超时逻辑
        if (dailyMeetings != null)
        {
            double durationThreshold = double.Parse(adminSettings[0].Setting_First_Value);
            foreach (var meeting in dailyMeetings)
            {
                if (meeting.End_Time == null)
                {
                    var elapsedMinutes = Math.Round((DateTime.Now - meeting.Start_Time).TotalMinutes, 1);
                    if (elapsedMinutes >= durationThreshold)
                    {
                        meeting.End_Time = DateTime.Now;
                        meeting.Duration = elapsedMinutes;
                        // 异步更新SQL,不阻塞Timer线程
                        await Task.Run(() => Function_Update_SQL.Update_Daily_Meeting(meeting));
                        // 线程安全更新本地状态
                        lock (Daily_Meeting_Summary)
                        {
                            Daily_Meeting_Summary[meeting.Division] = "True";
                        }
                    }
                }
            }
        }

        // 在UI线程执行权限验证(涉及UI操作)
        await Application.Current.Dispatcher.InvokeAsync(VerifyUserPermission);

        // 判断是否继续运行Timer
        bool allMeetingsCompleted = false;
        lock (Daily_Meeting_Summary)
        {
            allMeetingsCompleted = Daily_Meeting_Summary.All(x => x.Value == "True");
        }

        if (allMeetingsCompleted)
        {
            StopTimer();
        }
        else if (_isTimerRunning)
        {
            _meetingTimer.Change(_tickIntervalMs, Timeout.Infinite);
        }
    }
    catch (Exception ex)
    {
        // 记录异常(替换成你的日志逻辑)
        // Console.WriteLine($"Timer回调异常: {ex.Message}");
        // 异常后继续尝试运行Timer
        if (_isTimerRunning)
        {
            _meetingTimer.Change(_tickIntervalMs, Timeout.Infinite);
        }
    }
}

3. 异步SQL查询封装

把同步SQL查询包装成异步方法,避免阻塞Timer线程:

private async Task<List<Admin_Settings>> GetAdminSettingAsync(string setting)
{
    return await Task.Run(() =>
    {
        switch (setting)
        {
            case "Daily_Duration":
                return Function_Select_SQL.Get_Admin_Settings("Daily_Duration");
            case "Daily_Days_Setting":
                return Function_Select_SQL.Get_Admin_Settings("Daily_Days_Setting");
            default:
                return null;
        }
    });
}

private async Task<List<Daily_Meeting>> GetDailyMeetingAsync(string filter, DateTime date)
{
    return await Task.Run(() => Function_Select_SQL.Get_Daily_Meeting("", "", date));
}

4. 修正后的权限验证函数

确保UI操作在UI线程执行,同时正确控制Timer和UI状态:

private void VerifyUserPermission()
{
    var adminSettings = Function_Select_SQL.Get_Admin_Settings("Daily_Days_Setting");
    if (adminSettings == null || adminSettings.Count == 0)
    {
        StopTimer();
        return;
    }

    string[] dailyDays = adminSettings[0].Setting_First_Value.Split(',');
    if (!dailyDays.Contains(DateTime.Now.DayOfWeek.ToString()))
    {
        StopTimer();
        return;
    }

    // 线程安全更新会议状态摘要
    lock (Daily_Meeting_Summary)
    {
        if (Daily_Meeting_Summary.Count == 0)
        {
            foreach (var division in List_Divisions)
            {
                var meetings = Function_Select_SQL.Get_Daily_Meeting("", division, DateTime.Now);
                Daily_Meeting_Summary.Add(division, meetings == null || meetings[0].End_Time == null ? "False" : "True");
            }
        }
        else if (!string.IsNullOrEmpty(Division_Selected))
        {
            var meetings = Function_Select_SQL.Get_Daily_Meeting("", Division_Selected, DateTime.Now);
            Daily_Meeting_Summary[Division_Selected] = meetings == null || meetings[0].End_Time == null ? "False" : "True";
        }
    }

    // 控制UI显示
    bool allCompleted = Daily_Meeting_Summary.All(x => x.Value == "True");
    if (allCompleted)
    {
        StartDailyButton.Visibility = Visibility.Collapsed;
        Daily_Meeting_sb.Begin(); // 启动闪烁动画
        StopTimer();
    }
    else
    {
        StartDailyButton.Visibility = Visibility.Visible;
        if (!_isTimerRunning)
        {
            StartTimer();
        }
    }
}

关键注意事项

  • 线程安全:所有共享集合(如Daily_Meeting_Summary)的读写必须加锁,避免多线程冲突。
  • 异步优先:所有IO操作(SQL、文件等)都要异步执行,不要用.Result或.Wait()阻塞线程。
  • 异常捕获:Timer回调必须捕获所有异常,否则一次未处理的异常会导致Timer永久停止。
  • UI隔离:任何UI元素的访问和操作必须通过Dispatcher切换到UI线程执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 23:55:56