繁体   English   中英

每线程单例模式

[英]Per thread singleton pattern

在我的工作中,我偶然发现了这样一个设计问题:

  • 我需要每个线程一个Manager类的实例
  • 这些实例应该是全局可访问的,就像通过静态函数的单例模式一样
  • 每个线程可能需要使用不同的参数初始化其实例
  • 这些实例的生命周期应该是可控的,有时删除实例并允许GC收集它是有益的

如果存在这样的事情,前两点会使它成为“每线程单身”。

这就是我提出的(代码简化,我省略了安全检查等):

public class Manager {
  private final static ThreadLocal<Manager> local = new ThreadLocal<Manager>();

  private int x;
  Manager(int argument) { x = argument; }

  public static void start(int argument) { local.set(new Manager(argument); }
  public static void clean() { local.remove(); }

  private void doSomething1() { x++; .... }
  private int doSomething2() { if (--x == 0) clean(); ... }

  public static void function1() { local.get().doSomething1(); }
  public static int function2() { return local.get().doSomething2(); }
}

如您所见,也可以在私有方法中调用clean函数。 另请注意,通过使用静态函数,实例的引用永远不会泄露,因此分配给不同线程的实例不会混淆。

这工作得很好,但后来又有了另一个要求:

  • 不同的线程可能需要使用Manager类的不同实现

所以我定义了一个接口:

public interface ManagerHandler {
  void method1();
  int method2();
}

并修改了Manager类:

public class Manager {
  private final static ThreadLocal<ManagerHandler> local = new ThreadLocal<ManagerHandler>();

  public static void start(int argument) {
    ManagerHandler handler;
    // depending on the context initialize handler to whatever class it is necessary
    local.set(handler); 
  }
  public static void clean() { local.remove(); }

  public static void function1() { local.get().method1(); }
  public static int function2() { return local.get().method2(); }
}

示例实现如下所示:

public class ExampleManagerImplementation implements ManagerHandler {
  private int x;
  public ExampleManagerImplementation(int argument) { x = argument; }
  public void method1() { x++; .... }
  public int method2() { if (--x == 0) Manager.clean(); ... }
}

Manager类在此处作为外观,将所有调用转发到适当的处理程序。 这种方法存在一个大问题:我需要在Manager类和ManagerHandler接口中定义所有函数。 不幸的是, Manager类无法实现ManagerHandler接口,因为它具有静态函数而不是方法。

问题是:你能想到一个更好/更简单的方法来实现我上面列出的所有目标,而这个目标是没有这个问题的吗?

您无能为力,因为您基本上需要通过静态方法代理接口方法。 我只能想到两种不同方式实现相同功能的方法:

  1. 如果你正在使用DI框架,你可以摆脱静态Manager并使用一个注入的ManagerHandler实现,它将包含ThreadLocal
  2. 使用ManagerHandler接口中的方法生成(如'字节码生成')静态ManagerAccess类。

就个人而言,我不会想到将静态ManagerAccess类(包含ThreadLocal )作为一个严重的设计问题。 至少只要它保持自己的职责集(访问线程范围的实例和代理调用),并且不冒险在其他任何地方。

如果您正在使用此设计, Manager是否有必要完全隐藏ManagerHandler接口,或者您是否需要公开它以便您不必委托每个方法?

class Manager {
    public static ManagerHandler getHandler() { return local.get(); }
}

为每个线程类创建单例的技巧是在私有静态_current字段上使用ThreadStatic属性,这使得它由线程限定。 这样,_current字段将存储在线程内存中,而其他线程无法访问,而AppDomain则不是共享内存。 因此,它仅在线程范围内可用。 另一方面,Current属性可以在该AppDomain中的所有线程上访问,但是当它被调用时,它将返回该线程的正确实例。 这是您需要的代码:

public sealed class Manager
{
    // As you are using the ThreadStatic here you cannot
    // call the static constructor or use the Lazy implimentation for 
    // thread-safty and you have to use the old fashin Lock and anti-pattern.
    private static readonly object _criticalArea = new object();

    [ThreadStatic]
    private static Manager _current;
    public static Manager Current
    {
        get
        {
            if (_current == null)
            {
                lock (_criticalArea)
                {
                    if (_current == null)
                    {
                        _current = new Manager();
                    }
                }
            }
            return _current;
        }
    }

    private Manager()
    {
    }

    public string WhatThreadIsThis { get; set; }
}

[TestClass]
public class SingeltonPerThreadTest
{
    private readonly EventWaitHandle _threadHandler = new EventWaitHandle(false, EventResetMode.AutoReset);
    private string _sharedMemory = "I am the shared memory and yet in main thread :(";

    [TestMethod]
    public void TestSingeltonPerThread()
    {
        // Creates a _current for main thread.
        Manager.Current.WhatThreadIsThis = "I am the main thread :)";

        // Start another thread.
        (new Thread(CallTheThreadBaseSingelton)).Start();

        // Wait for it to be finished.
        _threadHandler.WaitOne();

        Assert.AreEqual("I am the main thread :)", Manager.Current.WhatThreadIsThis, "I am not the main thread :( ");
        Assert.AreEqual("I am the other thread ;)", _sharedMemory, _sharedMemory);
    }

    private void CallTheThreadBaseSingelton()
    {
        // Creates a _current for this thread (this thread is the other one :)) ).
        Manager.Current.WhatThreadIsThis = "I am the other thread ;)";
        _sharedMemory = Manager.Current.WhatThreadIsThis;
        _threadHandler.Set();
    }
}

干杯。

暂无
暂无

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

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