7

我有一种情况,为了测试,我只希望我的计时器方法(FooMethod)一次运行一个。在下面的示例中,FooMethod 作为委托传递给计时器。这个类有很多具体的实例。我认为通过使 _locker 静态,一次只能处理一个 FooMethod() 实例。但是当我运行应用程序时,多个线程一次通过 TryEnter() 行。

这就是我将每个类添加到新计时器的方式。这是在循环中为每个 foo 实例完成的:

_timers.Add(new Timer(foo.FooMethod, null, 0, 10000));

这是具有该方法的类:

public class Foo<T>
{
    private static readonly object _locker = new object();

    public void FooMethod(object stateInfo)
    {
        // Don't let threads back up; just get out
        if (!Monitor.TryEnter(_locker)) { return; }

        try
        {
            // Logic here
        }
        finally
        {
            Monitor.Exit(_locker);
        }
    }
}

注意:通常,_locker 不是静态的;我不希望同一个线程在有机会完成之前进入该方法。我在这里将其更改为静态以进行测试。

我的第一个想法是,也许这不起作用,因为该类是通用的?并且每个具体类实际上都是它自己的类并且它们不共享 _locker 变量?真的吗?如果这是真的,我应该如何让具体类共享一个 _locker 变量?我是否需要将静态 _locker 变量添加到 Foos 可以访问的其他类?

4

4 回答 4

7

我是否需要将静态 _locker 变量添加到 Foos 可以访问的其他类?

是的。

每个Foo<T>具有不同T参数的封闭类型都有自己的静态 _locker 对象。您可以使 Foo 从基类继承,并将静态对象放在那里。然后,所有类型都将使用相同的实例。

于 2012-04-16T20:28:13.187 回答
6

也许

public class Foo
{
   protected static readonly object _locker = new object();
}

public class Foo<T> : Foo
{
    public void FooMethod(object stateInfo)
    {        
        if (!Monitor.TryEnter(_locker)) { return; }

        try
        {
            // Logic here
        }
        finally
        {
            Monitor.Exit(_locker);
        }
    }
}
于 2012-04-16T20:29:17.373 回答
2

你是对的。代码中引用的每个唯一类型T都会导致 CLR 为其生成一个新的具体类型,Foo<T>并且每个类型都有自己的一组静态成员。

您可以将代码重组为如下所示。它只是众多有效变体中的一种。

public class Foo
{
    private static readonly object _locker = new object();

    public void FooMethod(object stateInfo)
    {
        // Don't let threads back up; just get out
        if (!Monitor.TryEnter(_locker)) { return; }

        try
        {
            // Logic here
        }
        finally
        {
            Monitor.Exit(_locker);
        }
    }
}

public class Foo<T>
{
    public void FooMethod(object stateInfo)
    {
        Foo.FooMethod(stateInfo);
    }
}

另外,请记住,您可以无限启动计时器,period以防止回调执行多次。Change在结束时再次调用以再次FooMethod排队计时器。由于您有多个计时器同时运行,您仍然会有多个同时执行的并发执行FooMethod,但至少现在每个计时器只有一个活动调用。这不是你所要求的,但我想我还是会指出这一点。

_timers.Add(new Timer(foo.FooMethod, _timers.Count, 10000, Timeout.Infinite));

public class Foo<T>
{
    public void FooMethod(object stateInfo)
    {
        try
        {
            // Logic here
        }
        finally
        {
            int index = (int)stateInfo;
            _timers[index].Change(10000, Timeout.Infinite);
        }
    }
}
于 2012-04-16T20:34:01.293 回答
0

请将此类设为非泛型类型。这将满足您的需求。

public class Foo
{
    private static readonly object _locker = new object();

    public void FooMethod(object stateInfo)
    {
        // Don't let threads back up; just get out
        if (!Monitor.TryEnter(_locker)) { return; }

        try
        {
            // Logic here
        }
        finally
        {
            Monitor.Exit(_locker);
        }
    }
}
于 2012-04-16T20:33:28.063 回答