在我的MVC 3 C#应用程序中,我有一些静态对象,我希望一次可用于一个请求。只能通过方法访问它,但我希望在调用其方法之间保持锁定。
调用只在控制器中完成,通常会有一个或两个锁定的代码块。
首先,我想暴露一些静态公共对象,并像
一样使用它lock(MyClass.lockObject)
{
MyClass.doStuff();
MyClass.doStuff2();
}
,但我发现它容易出错,因为我可能忘记将其锁定在某个地方。我想知道在构造函数中使用Monitor.Enter()
是否正确,在Dispose方法中使用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();
});
答案 0 :(得分: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();
});
答案 1 :(得分:1)
使用Monitor.Enter和Exit直接锁定更容易,更不容易出错。
从您的示例中不清楚您要尝试同步的内容。
在构造函数中调用Monitor.Enter并在Dispose中退出不是一个好主意。如果无法正确构造类,则必须在c'tor中处理所有异常并调用Exit。对一个实例进行锁定是没有意义的 - 这实际上意味着通过锁定c'tor。您可能希望查看Synchronized属性;但我不认为这是真的推荐。
答案 2 :(得分:1)
立即执行静态对象上其他对象的请求是否至关重要?如果您的静态对象维护一个它自己运行的队列,则可以通过线程隔离实现互斥。在来自另一个对象的调用中,请求的工作被放置在队列中,而在一个单独的线程中,静态对象正在通过队列工作(请注意,需要对队列进行互斥访问!)执行请求。
您可以在将工作添加到队列的方法中使用调用对象块,直到静态对象通知为止,或者提供回调接口以允许静态对象通知调用对象他们的工作已完成。 / p>
答案 3 :(得分: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
}
现在,lock是使用Monitor类的快捷方式。实际上翻译为:
bool getLock = false;
try {
Monitor.Enter(locker, ref getLock);
// do stuff here
}
finally {
if(getLock) {
Monitor.Exit(locker);
}
}
这样可以让您更好地控制对象的状态。内部代表, 好像发生了错误,你可以恢复到你对象的先前状态。
答案 4 :(得分:1)
如果调用的组合不多,你可以将dostuff和doStuff2设为私有,并在你的类中使用锁定放置包装函数
static public void doStuffs()
{
lock (lockObject)
{
doStuff();
doStuff2();
}
}