debugging 使用监视器查找以下信号量实现中的错误

bqucvtff  于 2022-11-14  发布在  其他
关注(0)|答案(2)|浏览(151)

在一次采访中,被要求识别以下代码中的bug(如果有)。
下面的类实现了具有一定容量的信号量。

public class MySemaphore
    {
        private object _mutex = new object();
        private int _currAvail;

        public MySemaphore(int capacity)
        {
            _currAvail​ = capacity;
        }

        public void Wait()
        {
            lock (_mutex)
            {
                if (_currAvail == 0) Monitor.Wait(_mutex);
                _currAvail--;
            }
        }

        public void Signal()
        {
            lock (_mutex) {
                _currAvail​++;
                Monitor.Pulse(_mutex);
            }
        }

    }
euoag5mw

euoag5mw1#

充分披露:我不使用C#。我不熟悉Monitor.Wait(o)Monitor.Pulse(o),但我相信,如果允许两个或更多线程同时调用sema.Wait()_currAvail可能会变为负值。
假设线程A和线程C同时调用sema.Wait(),而线程B调用sema.Signal()

currAvail  mutex   Thread A       Thread B        Thread C
---------  -----   -------------  --------------  --------------
    0      free    lock(mutex)         .               .
    0        A     Await Pulse()  Await mutex          .
    0      free      ''    ''     lock(mutex)          .
    0        B       ''    ''     currAvail++     Await mutex
    1        B       ''    ''     Pulse()          ''    ''

    1        B     Await mutex    release(mutex)   ''    ''
    1      free      ''    ''          .          lock(mutex)
    1        C       ''    ''          .          curAvail--
    0        C       ''    ''          .          release(mutex)
    0      free      ''    ''          .               .

    0      free    lock(mutex)         .               .
    0        A     currAvail--         .               .
   -1        A     release(mutex)      .               .
   -1      free          .             .               .

修复方法是在Monitor.Wait(_mutex)返回后重新检查_currAvail。即,在循环中调用Monitor.Wait(_mutex)

lock (_mutex) {
    while (_currAvail == 0) Monitor.Wait(_mutex);
    _currAvail--;
}
lmyy7pcs

lmyy7pcs2#

我发现此信号量存在以下问题:

  • capacity实际上只是初始容量,而不是总容量。因此,只需调用Signal 1000次,就可以获得1000个额外容量。
  • 未处理整数溢出。如果将int.MaxValue作为初始容量,然后将Signal作为初始容量,则当前容量将为int.MinValue
  • 构造函数不检查传入的int是否为负

相关问题