C#锁定导致冻结

本文关键字:冻结 锁定 | 更新日期: 2023-09-27 18:21:43

我有这个声音列表:

     List<SourceVoice> runningInstances;

我将一个事件附加到一个声音对象,以便在它停止时将其从列表中删除。

    sourceVoice.StreamEnd += delegate
            {
                lock (runningInstances)
                {
                    runningInstances.Remove(sourceVoice);
                }
            };

我还有一个stop函数,它可以从任何线程调用。

    public void stop(int fadeoutTime)
    {
        lock (runningInstances)
        {
            foreach (var sourceVoice in runningInstances)
            {
                if (!sourceVoice.IsDisposed)
                {
                    sourceVoice.Stop();
                    sourceVoice.FlushSourceBuffers();
                    sourceVoice.DestroyVoice();
                    sourceVoice.Dispose();
                }
            }
            runningInstances.Clear();
        }
    }

我认为,由于我将事件作为委托,它将一直等到对象解锁。然而,它似乎冻结在那里。

C#锁定导致冻结

有两种可能性:

  1. 在与CCD_ 1相同的线程上引发该事件。lock() {}没有任何功能,因为它是可重入的,但它也是无害的。调用Clear()时,这些项应该已经被删除。

  2. 在另一个(线程池)线程上引发该事件。这取决于sourceVoice.Stop()lock()将阻止事件处理,直到runningInstances.Clear()之后。之后,处理程序将运行,从epty List<>中删除不会出错。

两者都不会导致任何"冻结",所以代码中一定有我们看不到的相关内容。

委托只是回调,它们对线程没有任何保证。您可能想要签出ConcurrentBag类,它已经是线程安全的,这样就可以避免对集合的锁定过于担心。

看起来stop方法的锁定范围内的一个调用可能导致触发StreamEnd事件。您可以通过遍历stop方法中的代码来测试这一点,看看它是否跳到事件中。我大胆猜测这是sourceVoice.Stop()调用。

如果sourceVoice.Stop()总是引发sourceVoice.StreamEnd事件,则可以按如下方式更改停止方法。

public void stop(int fadeoutTime)
    {
            foreach (var sourceVoice in runningInstances.ToList<SourceVoice>())
            {
                if (!sourceVoice.IsDisposed)
                {
                    sourceVoice.Stop();
                    sourceVoice.FlushSourceBuffers();
                    sourceVoice.DestroyVoice();
                    sourceVoice.Dispose();
                }
            }
    }

要了解.ToList(),您可以查看

ToList()--它是否创建一个新列表?