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

如何实现仅当数据库重连窗口关闭时阻塞并发线程?

解决并发线程下数据库重连弹窗的阻塞问题

嘿,我来帮你搞定这个并发场景下的弹窗重复弹出和线程阻塞问题!你的核心需求是:当多个线程同时触发数据库连接失败时,只弹出一个错误窗口,其他线程阻塞等待窗口处理完成,同时不能让窗口本身因为阻塞而卡死对吧?

问题根源分析

你之前尝试用EventWaitHandle时窗口被阻塞,大概率是因为把等待逻辑放到了UI线程里——UI线程一旦被阻塞,窗口自然就无法响应操作了。另外,没有全局的同步机制来控制弹窗的创建,导致多个线程同时弹出窗口。

可行解决方案

我们需要结合全局锁和事件等待句柄,同时严格区分UI线程和后台线程的职责,确保UI操作永远在Dispatcher线程执行,后台线程只处理等待和重连逻辑。

第一步:添加全局同步变量

先在类里定义几个静态的同步变量,用来控制弹窗的唯一性和线程等待:

// 全局锁,控制弹窗创建逻辑的并发访问
private static readonly object _reconnectSyncLock = new object();
// 事件等待句柄,让其他线程等待弹窗处理完成
private static EventWaitHandle _reconnectWaitEvent;
// 标记是否已有重连弹窗正在显示
private static bool _isReconnectWindowActive = false;
// 确保连接对象的线程安全访问
private static readonly object _connectionInstanceLock = new object();
private NpgsqlConnection _conexao;
public NpgsqlConnection conexao 
{ 
    get { lock(_connectionInstanceLock) return _conexao; }
    set { lock(_connectionInstanceLock) _conexao = value; }
}

第二步:重构重连方法

修改ReconectarDB方法,加入同步控制逻辑,确保只有第一个失败的线程弹出窗口,其他线程等待窗口处理结果:

public bool ReconectarDB(string strConexao)
{
    bool conexaoFalhou = false;

    // 第一步:检查是否已有弹窗,有则等待
    lock (_reconnectSyncLock)
    {
        if (_isReconnectWindowActive)
        {
            // 等待弹窗处理完成的信号
            _reconnectWaitEvent.WaitOne();
            // 等待结束后,返回当前连接状态
            return conexao != null && conexao.State == System.Data.ConnectionState.Open;
        }
        // 标记弹窗即将创建
        _isReconnectWindowActive = true;
        _reconnectWaitEvent = new AutoResetEvent(false);
    }

    // 第二步:后台执行重连尝试
    var reconnectTask = Task.Run(() =>
    {
        int tentativas = 0;
        while (tentativas <= 4)
        {
            Thread.Sleep(1000);
            try
            {
                var novaConexao = new NpgsqlConnection(strConexao);
                novaConexao.Open();
                conexao = novaConexao;
                conexaoFalhou = false;
                break;
            }
            catch
            {
                tentativas++;
                conexaoFalhou = true;
            }
        }
    });

    // 第三步:重连完成后的处理
    reconnectTask.ContinueWith(task =>
    {
        lock (_reconnectSyncLock)
        {
            _isReconnectWindowActive = false;
        }

        Application.Current.Dispatcher.Invoke(() =>
        {
            if (conexaoFalhou)
            {
                // 只弹出一次错误窗口
                var erroDialog = new DialogErroConexao(ErroConexao.SemConexao);
                var resultado = erroDialog.ShowDialog();

                if (resultado == true)
                {
                    // 用户点击重试,递归调用重连
                    ReconectarDB(strConexao);
                }
                else
                {
                    // 用户选择终止系统,安全退出
                    Application.Current.Shutdown();
                }
            }
            else
            {
                // 重连成功,关闭加载弹窗
                if (DialogReconectando != null && DialogReconectando.IsLoaded)
                {
                    DialogReconectando.Close();
                }
            }

            // 通知所有等待的线程:弹窗处理完成
            _reconnectWaitEvent.Set();
        });
    });

    // 第四步:显示重连加载弹窗(仅第一次触发时显示)
    Application.Current.Dispatcher.Invoke(() =>
    {
        if (DialogReconectando == null || !DialogReconectando.IsLoaded)
        {
            DialogReconectando = new DialogErroConexao(ErroConexao.Reconectando);
            DialogReconectando.ShowDialog();
        }
    });

    return conexaoFalhou;
}

关键细节说明

  1. 全局锁的作用:_reconnectSyncLock确保同一时间只有一个线程能进入弹窗创建逻辑,避免多个线程同时弹出窗口。
  2. 事件等待句柄:_reconnectWaitEvent让后续触发重连的线程进入等待状态,直到弹窗处理完成后收到信号才继续执行。
  3. UI线程隔离:所有弹窗的创建和操作都通过Application.Current.Dispatcher.Invoke放到UI线程执行,避免阻塞UI导致窗口无响应。
  4. 连接对象线程安全:用_connectionInstanceLock包裹连接对象的读写,避免多线程访问时出现状态不一致的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:21:14