代码之家  ›  专栏  ›  技术社区  ›  Jimmy

C语言中的线程安全可破坏事件触发类#

  •  2
  • Jimmy  · 技术社区  · 15 年前

    最近,我被要求实现一个类作为选择过程的一部分。我按要求做了这个节目。但是,我考试不及格。我真的很想知道我的解决方案出了什么问题。任何帮助都非常感谢。问题和我的解决方案如下

    实现一个线程安全类,该类在构造过程中每秒触发一个事件。需要有一个函数来查找经过的秒数。这个类必须实现IDisposable,调用dispose后对seconds elapsed函数的任何调用都应该失败。

    我的解决方案:

    namespace TimeCounter
    {
    public delegate void SecondsElapsedHandler(object o, EventArgs e);
    /// <summary>
    /// Summary description for SecondCounter
    /// </summary>
    public class SecondCounter : IDisposable
    {
        private volatile int nSecondsElapsed;
        Timer myTimer;
        private readonly object EventLock = new object();
        private SecondsElapsedHandler secondsHandler;
        public SecondCounter()
        {
            nSecondsElapsed = 0;
            myTimer = new Timer();
            myTimer.Elapsed += new ElapsedEventHandler(OneSecondElapsed);
            myTimer.Interval = 1000;
            myTimer.AutoReset = false;
            myTimer.Start();
        }
    
        public void OneSecondElapsed(object source, ElapsedEventArgs e)
        {
            try
            {
                SecondsElapsedHandler handlerCopy;
                lock (EventLock)
                {
                    handlerCopy = secondsHandler;
                    nSecondsElapsed++;
    
                }
                if (secondsHandler != null)
                {
                    secondsHandler(this, e);
                }
            }
            catch (Exception exp)
            {
                Console.WriteLine("Exception thrown from SecondCounter OneSecondElapsed " + exp.Message);
            }
            finally
            {
                if (myTimer != null)
                {
                    myTimer.Enabled = true;
                }
            }
        }
    
        public event SecondsElapsedHandler AnotherSecondElapsed
        {
            add
            {
                lock (EventLock)
                {
                    secondsHandler += value;
                }
            }
            remove
            {
                lock (EventLock)
                {
                    secondsHandler -= value;
                }
    
            }
        }
    
        public int SecondsElapsed()
        {
            if (this.IsDisposed)
            {
                throw new ObjectDisposedException("SecondCounter");
            }
            return nSecondsElapsed;
    
        }
    
        private bool IsDisposed = false;
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        private void Dispose(bool Disposing)
        {
            if (!IsDisposed)
            {
                if (Disposing)
                {
    
                }
                if (myTimer != null)
                {
                    myTimer.Dispose();
                }
    
            }
            secondsHandler = null;
            IsDisposed = true;
    
        }
        ~SecondCounter()
        {
            Dispose(false);
        }
    }
    }
    
    2 回复  |  直到 15 年前
        1
  •  2
  •   Dan Bryant    15 年前

    有几个问题:

    1. 您可能会因为一般的异常吞入而受到惩罚,尽管这与线程问题没有特别的关系。

    2. 计时器上存在竞争条件。请释放,因为您可以在计时器重新设置为启用之前释放它,从而导致异常。

    3. 您从未在Dispose中将myTimer设置为空。

    4. 不需要显式实现带有锁定的事件。委托是不可变的,添加/删除事件永远不会导致无效的委托状态,不过,如果在触发回调的同时添加/删除委托,则可能会出现竞争条件。如果使用标准的“public event”声明而没有显式的备用私有委托,则将自动处理同步。

    5. (小点)如果您要实现完整的Dispose模式,通常会将Dispose(bool disposing)方法标记为 protected virtual ,以便派生类可以挂接到处理机制中。更好的是,给你的班级打分 sealed 你可以完全消除终结器。

        2
  •  0
  •   Daniel Earwicker    15 年前

    false 作为 Disposing 参数。这应该说明 Dispose(bool) 以避免尝试释放其他托管对象。但在这种方法中,你说:

    if (Disposing)
    {
    
    }
    if (myTimer != null)
    {
        myTimer.Dispose();
    }
    

    处置 . 这意味着你给计时器打电话 Dispose

    事实上,通常建议这些天不要编写终结器。这个问题不是要你写的!不幸的是大多数关于 IDisposable 同时也要讨论终结器。它们是不同的主题。

    你也抓住了 Exception ,通用异常基类。这意味着你能捕捉到 NullReferenceException . 通常不是个好主意。您还可以登录到控制台,这在基于GUI或服务器的应用程序中不太值钱。

    您可以替换:

    myTimer.Elapsed += new ElapsedEventHandler(OneSecondElapsed);
    

    使用:

    myTimer.Elapsed += OneSecondElapsed;
    

    the Microsoft guidelines .

    推荐文章