C# 线程消失,finally 块未执行

问题描述 投票:0回答:1

我有一个 C# 应用程序来处理 PLC(控制硬件设备的西门子 cpu)事件。所有事件都在单独的线程中处理,所有这些线程都是一个大的 try/catch/finally 块来锁定数据库,然后最终释放锁。它运行良好,直到我放置 Thread.Sleep() 以避免处理重复的 PLC 事件(因为有时 PLC 触发事件但数据仍未更新,所以如果我发现数据与以前的数据相同,我想休眠 1 秒)。

我放置了一些日志来调试这个问题,我发现处理线程在没有运行finally块的情况下消失了。线程main()是这样的:

virtual protected void Thread1Main()
{
    DB.DeviceLock(config.position);
    if (config.position == 4)
        Logger.I("DeviceLock 4");
    try
    {
        if (_exchange.plc_long > 0 && _exchange.plc_short > 0 && _exchange.plc_long == _last_long && _exchange.plc_short == _last_short)
        {
            _last_long = 0;
            _last_short = 0;
            if (config.position == 4)
                Logger.I("Before sleep 4");
            Thread.Sleep(1000);
            if (config.position == 4)
                Logger.I("After sleep 4");
            return;
        }
        _last_long = _exchange.plc_long;
        _last_short = _exchange.plc_short;
        .......
        if (config.position == 4)
            Logger.I("end 4");
    }
    catch (Exception ex)
    {
        if (config.position == 4)
            Logger.I("exception 4");
        Logger.E(this, ex.Message);
    }
    finally
    {
        if (config.position == 4)
            Logger.I("finally 4");
        DB.DeviceUnlock(config.position);
        _thread = null;
    }
}

这里我有 DB.DeviceLock() 仅锁定此设备,并且我有 DLock() 锁定所有设备。 DB代码是这样的:

static public void DBLock()
{
    Logger.I("DBLock:Before lock");
    lock (_locker)
    {
        while (_deviceLocks.MyCount() > 0)
        {
            foreach (var d in _deviceLocks.MyToArray())
                Logger.W($"DBLock:Wait device {d} to unlock");
            Thread.Sleep(1000);
        };
        _dbLock = 1;
        Logger.I("DBLock:Locked");
    }
}

static public void DBUnlock()
{
    Logger.I("DBLock:Unlocked");
    _dbLock = 0;
}

static public void DeviceLock(int d)
{
    lock(_locker)
    {
        while (_dbLock > 0)
        {
            Logger.W($"DBLock:Device {d} waiting DBLock");
            Thread.Sleep(1000);
        };
        _deviceLocks.MyAdd(d);
    }
}

static public void DeviceUnlock(int d)
{
    _deviceLocks.MyRemove(d);
    if (_deviceLocks.MyCount() > 0)
        Logger.W($"DBLock:released {d}, still locked: " + string.Join(",", _deviceLocks.MyToArray()));
}

在日志中,当我故意放入可疑数据以使其睡眠 - >处理 - >睡眠 - >处理时,我确实看到了正确的日志,即使执行了finally块:

I 16:58:47.798 :DeviceLock 4
I 16:58:47.798 :Before sleep 4
I 16:58:48.799 :After sleep 4
I 16:58:48.799 :finally 4
I 16:58:48.834 :DeviceLock 4
W 16:58:48.842 :DB locks:released 701, still locked: 4
I 16:58:48.848 :end 4
I 16:58:48.848 :finally 4
I 16:58:48.935 :DeviceLock 4
I 16:58:48.935 :Before sleep 4

问题是当我调用 DLock() 时,一切都会停止。 DLock一直等待设备4解锁,但是设备4线程消失了!它再也不会回来,它的finally块没有被执行,并且在调试模式下,我在Visual Studio的线程视图中找不到该线程。日志变成这样:

I 16:58:52.355 :DeviceLock 4   <-- this is the last log of device 4
I 16:58:53.766 :DBLock:Before lock
W 16:58:53.766 :DBLock:Wait device 4 to unlock
W 16:59:02.774 :DBLock:Wait device 4 to unlock

然后无穷无尽

W 16:59:02.774 :DBLock:Wait device 4 to unlock

所以我的问题是:为什么线程在没有运行finally块的情况下消失了?

添加更多信息:

我发现这段代码可能会导致问题:

public void RefreshAlertIcon(Device dev,int error)
{
    if (Dispatcher.CheckAccess())
    {
        if (_d2icon.ContainsKey(dev.config))
            _d2icon[dev.config].ShowAlert(error > 0 && !_d2icon[dev.config]._alert);
    }
    else
        Dispatcher.Invoke(delegate {
            if (_d2icon.ContainsKey(dev.config))
                _d2icon[dev.config].ShowAlert(error > 0 && !_d2icon[dev.config]._alert);
        });
}

RefreshAlertIcon 在 Thread1Main() 中被调用,..... 部分。那么 Dispatcher 导致线程消失,我是否正确使用 Dispatcher?

c# multithreading locking plc
1个回答
0
投票

最后我发现将Dispatcher.Invoke更改为InvokeAsync解决了问题。看来 Dispatcher.Invoke 有自己的锁,可能无法从 UI 线程本身调用。所以最后的代码如下所示:

    Dispatcher.InvokeAsync(delegate {
        if (_d2icon.ContainsKey(dev.config))
            _d2icon[dev.config].ShowAlert(error > 0 && !_d2icon[dev.config]._alert);
    });

我们的应用程序现在已经连续工作1周没有问题!太棒了!

© www.soinside.com 2019 - 2024. All rights reserved.