[英]Per thread singleton pattern
在我的工作中,我偶然發現了這樣一個設計問題:
Manager
類的實例 如果存在這樣的事情,前兩點會使它成為“每線程單身”。
這就是我提出的(代碼簡化,我省略了安全檢查等):
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函數。 另請注意,通過使用靜態函數,實例的引用永遠不會泄露,因此分配給不同線程的實例不會混淆。
這工作得很好,但后來又有了另一個要求:
所以我定義了一個接口:
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
接口,因為它具有靜態函數而不是方法。
問題是:你能想到一個更好/更簡單的方法來實現我上面列出的所有目標,而這個目標是沒有這個問題的嗎?
您無能為力,因為您基本上需要通過靜態方法代理接口方法。 我只能想到兩種不同方式實現相同功能的方法:
Manager
並使用一個注入的ManagerHandler
實現,它將包含ThreadLocal
。 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.