2012-08-02 40 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(); 
    } 
} 
相关问题