Monitor.TryEnter with Generic Class

时间:2012-04-16 20:20:22

标签: c# multithreading locking

我有一种情况,为了测试,我只希望我的计时器方法(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 个答案:

答案 0 :(得分:7)

  

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

是。

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

答案 1 :(得分: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);
        }
    }
}

答案 2 :(得分: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);
        }
    }
}

答案 3 :(得分: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);
        }
    }
}