2

最近,我被要求在选择过程中实现一个类。我按照要求做了程序。然而,我在测试中失败了。我真的很想知道我的解决方案有什么问题。任何帮助深表感谢。下面给出了问题和我的解决方案

问题:

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

我的解决方案:

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);
    }
}
}
4

2 回答 2

2

有几个问题:

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

  2. 您的 timer.Dispose 存在竞争条件,因为您可以在计时器再次设置为启用之前将其处置,从而导致异常。

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

  4. 您正在从终结器 (disposing=false) 访问托管类 myTimer,这是一个坏主意。

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

  6. (次要点)如果您正在实现完整的 Dispose 模式,习惯上将 Dispose(bool disposing) 方法标记为protected virtual,以便派生类可以挂钩到处置机制。更好的是,标记您的课程sealed,您可以完全消除终结器。

于 2010-11-02T21:05:43.860 回答
0

您的终结器可能已损坏。false它正确地作为Disposing参数传递。这应该告诉Dispose(bool)避免尝试处置其他托管对象。但是在那种方法中,您提出:

if (Disposing)
{

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

所以你忽略了 的值Disposing。这意味着您Dispose从终结器线程调用计时器的方法,而该对象可能已经被终结(如果它有终结器,它可能会这样做)。终结器以不可预测的顺序运行。通常建议不要从终结器调用其他 GC 管理的对象。

事实上,通常建议您现在不要编写终结器。这个问题没有要求你写一个!不幸的是,大多数教程IDisposable也谈到了终结器。他们是不同的科目。

您还可以 catch Exception,通用异常基类。这意味着您可以捕获诸如NullReferenceException. 通常不是一个好主意。您还可以登录到控制台,这在 GUI 或基于服务器的应用程序中没有多大价值。

您可以更换:

myTimer.Elapsed += new ElapsedEventHandler(OneSecondElapsed);

和:

myTimer.Elapsed += OneSecondElapsed;

您的变量命名不一致。请参阅Microsoft 指南

于 2010-11-02T21:12:49.647 回答