2012-08-02 77 views
4

在我的MVC 3 C#應用程序中,我有一個靜態對象,我希望一次對一個請求可用。只有通過方法才能訪問它,但是我希望鎖定在調用其方法之間。什麼是鎖定線程的正確方法?

調用只能在控制器中完成,通常會有一個或兩個鎖定的代碼塊。

起初我想揭露一些靜態公共對象,並用它只是像

lock(MyClass.lockObject) 
{ 
MyClass.doStuff(); 
MyClass.doStuff2(); 
} 

,但我覺得它容易出錯,因爲我可能會忘了某處將其鎖定。我想知道在一個Dispose方法中使用Monitor.Enter()Monitor.Exit()是否合適,然後將我的方法更改爲非靜態?我說,是這樣的:

public class MyClass:IDisposable 
{ 
    static protected object _locker = new object(); 
    protected bool isDisposed = false; 

    public MyClass() 
    { 
     Monitor.Enter(_locker); 
    } 

    public void Dispose() 
    { 
     if (!isDisposed) 
     { 
      Monitor.Exit(_locker); 
      GC.SuppressFinalize(this); 
      isDisposed = true; 
     } 
    } 

    ~SPInstances() 
    { 
     Dispose(); 
    } 

    public void doStuff() 
    { 
     if(isDisposed) throw new ObjectDisposedException(); 
     // do stuff here, etc. 
    } 
} 

然後,我可以用它作爲:

using(MyClass myinstance = new MyClass()) 
{ 
    myInstance.doStuff(); 
    myInstance.doStuff2(); 
} 

這樣,即使我忘了在使用包裝代碼,它仍然會鎖定並會有一些機會,這將在垃圾回收過程中得到解鎖...

我不擅長C#,有時會忽略某些方面,而線程從來不容易調試,所以我想知道我是否在一個好的軌道上。這是實現我的目標的正確方法嗎?

編輯:

擴展碩士道德觀念,將這種方式是更好的(我簡化了一點,因爲我只需要一個資源的情況下)?

public class MyClass 
{ 
    static protected readonly MyResourceType _myResourceStatic = new MyResourceType(); 
    static public void DoWork(Action<MyClass> action) 
    { 
     lock(_myResource) 
     { 
      action(new MyClass(_myResource)); 
     }   
    } 

    protected MyClass(MyResourceType myResource) 
    { 
     _myResource = myResource; 
    } 
    protected readonly _myResource; 

    public void DoFirstThing() { ... } 
    public void DoSecondThing(){ ... } 
} 

MyClass.DoWork(x => 
{ 
    x.DoFirstThing(); 
    // do unrelated stuff 
    x.DoSecondThing(); 
}); 
+1

「但我想上保持全鎖請求「 - 不會導致您的站點只能一次處理一個請求(至少有一個請求一次訪問此靜態對象)? – mbeckish 2012-08-02 14:23:14

+0

對不起,這太誇張了;我的意思是我希望它在方法調用之間鎖定,較長的請求將在開始時和最終處理時鎖定 – trakos 2012-08-02 14:28:48

+1

更準確地說明範圍。我不確定你有保證所有相關的代碼(Controller,View,ActionFilters)都能在同一個線程上運行。 – 2012-08-02 14:34:18

回答

2

恕我直言,最好是lock在你自己的方法。這樣,另一位程序員或以後自己在調用方法之前不必記住lock,這很簡單。

public class MyClass 
{ 
    private static readonly object _gate = new object(); 

    /* something that can only be accessed by one thread at a time...*/ 
    private static MyResourceType MyResource = new MyResourceType(); 

    public void DoSomething() 
    { 
     lock(_gate) 
     { 
      /* do something with MyResource, just make sure you 
       DO NOT call another method that locks the gate 
       i.e. this.DoSomethingElse(), in those situations, 
       you can take the logic from DoSomethingElse() and 
       toss it in a private method i.e. _DoSomethingElse(). 
      */ 
     } 
    } 

    private void _DoSomethingElse() 
    { 
     /* do something else */ 
    } 

    public void DoSomethingElse() 
    { 
     lock(_gate) 
     { 
      _DoSomethingElse(); 
     } 
    } 
} 

當天晚些時候...

var myClass = new MyClass(); 
myClass.DoSomething(); 
如果你想能夠調用多個方法用鎖

,你可以用一個lambda做到這一點, 併成爲真正的安全,包它在一個幫手類。

public class MyClass 
{ 
    public MyResourceType MyResource { get; set; } 
    public void DoFirstThing() { ... } 
    public void DoSecondThing(){ ... } 
} 

public class MyClassHelper 
{ 
    private static readonly object _gate = new Object(); 
    private static MyResourceType MyResource = new MyResourceType(); 

    private MyClass _myClass = new MyClass();   

    public void DoWork(Action<MyClass> action) 
    { 
     lock(_gate) 
     { 
      _myClass.MyResource = MyResource; 
      action(_myClass); 
      _myClass.MyResource = null; 
     } 
    } 
} 

... 

var myClassHelper = new MyClassHelper(); 
myClassHelper.DoWork(x => 
    { 
     x.DoFirstThing(); 
     x.DoSecondThing(); 
    }); 
+0

嗯,是的,我同意,但正如我所說的,我希望兩個調用方法之間保持鎖定,例如我不希望任何其他線程在調用doStuff, doStuff2,當然還有其他一些邏輯。所以,每當使用實例時,我都必須使用鎖定。 – trakos 2012-08-02 15:25:46

+1

我編輯我的例子,用一個助手,你可以調用任何你想要的方法。 – 2012-08-02 15:39:30

+0

看起來很有趣,但畢竟它不會阻止直接使用MyClass,所以它就像使用lock {}塊一樣,但是有助於輔助類的開銷? – trakos 2012-08-02 17:17:42

1

鎖是更容易和更容易出錯,直接使用Monitor.Enter和Exit。

從您的示例中不清楚您嘗試同步的內容。

這不是一個好主意,Monitor.Enter在構造函數和Exit in Dispose。如果不能正確構造類,則必須在c'tor內處理所有例外並調用Exit。一個實例被鎖定是沒有意義的 - 這實際上意味着通過鎖定c'tor。您可能想查看Synchronized屬性;但我不認爲這是真正的建議。

+0

構造函數中沒有邏輯;正如我所說的那樣,這些方法也可能是靜態的。構造函數和Dipose將不會有任何比這裏更多的例子。 – trakos 2012-08-02 14:50:14

1

立即執行其他對象對您的靜態對象的請求是否至關重要?如果您的靜態對象維護一個本身可以工作的隊列,則可以通過線程隔離實現互斥。在來自另一個對象的調用中,請求的工作被放置在隊列中,而在一個單獨的線程中,靜態對象正在通過隊列工作(請注意,需要對隊列進行互斥訪問,儘管!)執行請求。

您可以在將工作添加到隊列中的方法中阻止調用對象,直到靜態對象通知爲止,或者提供回調接口以允許靜態對象通知調用對象他們的工作已完成。

1

從你的例子並不完全清楚你想要做什麼。 作爲良好的編程習慣,最好讓每個單獨的方法獲得鎖定,並在完成關鍵部分時釋放它。 在你的情況將是:

void doStuff() 
{ 
    if(isDisposed) throw new ObjectDisposedException(); 
    // do stuff here, etc. 
    lock(_locker) { 
     // enter critical section here 
    } 
    // continue to do other stuff 
} 

void doStuff2() 
{ 
    if(isDisposed) throw new ObjectDisposedException(); 
    // do stuff here, etc. 
    lock(_locker) { 
     // enter critical section here 
    } 
    // continue to do other stuff 
} 

現在鎖是使用Monitor類短切版本。而實際上被轉換,如:

bool getLock = false; 
try { 
    Monitor.Enter(locker, ref getLock); 
    // do stuff here 
} 
finally { 
    if(getLock) { 
    Monitor.Exit(locker); 
    } 
} 

這將讓您對您的對象&內部表示, 彷彿有什麼問題發生,你可以恢復到對象的先前狀態的狀態更多的控制。

+0

嗯,是的,我同意,但正如我所說的,我想要一次爲兩個方法保留鎖,例如我不希望任何其他線程在調用doStuff, doStuff2,當然還有其他一些邏輯。所以,每當使用實例時,我都必須在實例外使用鎖。 – trakos 2012-08-02 15:23:34

+1

這實際上取決於您的使用目的。如果你認爲你總是一起調用這兩種方法,你可以寫第三種方法。並且該方法將按順序調用它們。所以這種方法將鎖定。如果doStuff()和doStuff2()長時間運行,它可能會導致爭用。例如: public void DoAllSuff(){ lock(_locker){ doStuff(); doStuff2(); } } 然後你只需要調用MyClass.DoAllStuff(),你就會得到你所需要的。 – Stas 2012-08-02 15:30:23

1

如果沒有電話的多種組合,你可以做dostuff和doStuff2私人和放置一個包裝函數在你的班上有鎖

static public void doStuffs() 
{ 
    lock (lockObject) 
    { 
     doStuff(); 
     doStuff2(); 
    } 
} 
相關問題