简体   繁体   English

Monitor.TryEnter with Generic Class

[英]Monitor.TryEnter with Generic Class

I have a situation where, for testing, I only want my timer method (FooMethod) to run one at a time. 我有一种情况,为了测试,我只希望我的计时器方法(FooMethod)一次运行一个。 In the example below, FooMethod is passed as the delegate to a timer. 在下面的示例中,FooMethod作为委托传递给计时器。 There are many concrete instances of this class. 这个类有很多具体的例子。 I thought that by making _locker static, only one instance of FooMethod() would process at a time. 我认为通过使_locker静态,只有一个FooMethod()实例会一次处理。 But when I run the app, multiple threads are getting past the TryEnter() line at a time. 但是当我运行应用程序时,多个线程一次超过TryEnter()行。

This is how I'm adding each class to a new timer. 这就是我将每个类添加到新计时器的方法。 This is done, in a loop, for each foo instance: 这是在一个循环中为每个foo实例完成的:

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

And this is the class that has that method: 这是具有该方法的类:

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

Note: Normally, _locker isn't static; 注意:通常,_locker不是静态的; I don't want the same thread entering the method before it got a chance to complete. 在有机会完成之前,我不希望同一个线程进入该方法。 I changed it to static here for testing. 我把它改为静态测试。

My first thought is that maybe this isn't working because the class is generic? 我的第一个想法是,这可能不起作用,因为这个类是通用的? And that each concrete class is actually its own class and they don't share the _locker variable? 并且每个具体类实际上是它自己的类,它们不共享_locker变量? Is that true? 真的吗? If that's true how should I have the concrete classes share a _locker variable? 如果这是真的,我应该如何让具体类共享一个_locker变量? Do I need to add a static _locker variable to some other class to which the Foos have access? 我是否需要将静态_locker变量添加到Foos可以访问的其他类中?

Do I need to add a static _locker variable to some other class to which the Foos have access? 我是否需要将静态_locker变量添加到Foos可以访问的其他类中?

Yes. 是。

Each closed Foo<T> type, with different T arguments, has its own static _locker object. 每个关闭的Foo<T>类型,具有不同的T参数,都有自己的静态_locker对象。 You could make Foo inherit from a base class, and put the static object there. 你可以让Foo继承自基类,并将静态对象放在那里。 Then, all the types would use the same instance. 然后,所有类型都将使用相同的实例。

Maybe 也许

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

You are correct. 你是对的。 Each unique type T referenced in code causes the CLR to generate a new concrete type for Foo<T> and each has its own set of static members. 代码中引用的每个唯一类型T都会导致CLR为Foo<T>生成新的具体类型,并且每个类型都有自己的静态成员集。

You could restructure your code to look like the following. 您可以将代码重构为如下所示。 It is but one among many valid variations. 它只是众多有效变体中的一种。

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

Also, keep in mind that you can start the timer with an infinite period to prevent the callback from executing more than once. 另外,请记住,你可以无限启动定时器period ,以防止回调执行不止一次。 Call Change again at the end of FooMethod to queue the timer again. FooMethod结束时再次调用Change再次对计时器进行排队。 Since you have multiple timers all going at once you will still have multiple concurrent executions of FooMethod going simultaneously, but at least now there will only be one active call per timer. 由于你有多个计时器同时进行,你仍然会同时执行FooMethod多个并发执行,但至少现在每个计时器只有一个活动的调用。 That is not exactly what you asked for, but I thought I would point this out anyway. 这不完全是你所要求的,但我想我无论如何都会指出这一点。

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

Please make this class as non-generic type. 请将此类设为非泛型类型。 That would purpose your need. 这将满足您的需求。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM