简体   繁体   English

锁定线程的正确方法是什么?

[英]what is a proper way to lock threads?

In my MVC 3 C# application I have some static object that I want to be available to one request at a time. 在我的MVC 3 C#应用程序中,我有一些静态对象,希望一次可用于一个请求。 Access to it is only through methods, but I want the lock to be kept in between calling its methods. 只能通过方法访问它,但是我希望在调用其方法之间保持锁。

Calls will be done only in a controller, usually there will be one or two locked blocks of code. 调用将仅在控制器中完成,通常会有一个或两个锁定的代码块。

At first I wanted to expose some static public object and use it simply like 首先,我想公开一些静态公共对象,并像

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

, but I find it error prone, as I might forget to lock it somewhere. ,但我发现它容易出错,因为我可能忘记将其锁定在某处。 I wonder if it is a proper way to use Monitor.Enter() in a constructor, and Monitor.Exit() in a Dispose method, and then change my methods to nonstatic? 我不知道它是用一种合适的方式Monitor.Enter()在构造和Monitor.Exit()的Dispose方法,然后改变我的方法,以非静态? Say, something like: 说,类似:

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.
    }
}

Then I could use it as: 然后我可以将其用作:

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

Then, even if I forgot to wrap code in using, it will still lock and there will be some chance that it will get unlocked during garbage collecting... 然后,即使我忘记使用代码来包装代码,它仍然会锁定,并且有可能在垃圾回收期间将其解锁...

I'm not proficient in C# and sometimes overlook some aspects, and threading is never easy to debug later on, so I'm wondering if I'm on a good track. 我不精通C#,有时会忽略某些方面,以后再也不容易调试线程,所以我想知道自己是否步入正轨。 Is it a proper way to achieve my goal? 这是实现我的目标的正确方法吗?

EDIT: 编辑:

Extending Master Morality idea, would this way be better (I simplified it a bit, as I need only one instance of resource)? 扩展道德大师的想法,这种方法会更好(我只需要一个资源实例,我就简化了一点)吗?

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();
});

IMHO it's better to lock inside your own methods. 恕我直言,最好将自己的方法lock在内部。 That way, another programmer, or yourself later, doesn't have to remember to lock before they call a method, and it's simple enough. 这样,另一个程序员或您自己以后就不必记住在调用方法之前就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();
         }
     }
}

Later that day... 那天晚些时候...

var myClass = new MyClass();
myClass.DoSomething();

if you want to be able to call multiple methods with a lock, you can do it with a lambda, and to be really safe, wrap it in a helper class. 如果您希望能够使用锁调用多个方法,则可以使用lambda进行操作,并且为了安全起见,请将其包装在helper类中。

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();
    });

lock is easier and less error prone that using Monitor.Enter and Exit directly. 与使用Monitor.Enter和Exit直接相比,锁定更容易且更不容易出错。

It's not clear from your example what you're trying to synchronize. 从您的示例尚不清楚您要同步的内容。

It is not a good idea to Monitor.Enter in a constructor and Exit in Dispose. Monitor不是一个好主意。在构造函数中输入,然后在Dispose中退出。 You will have to handle all exceptions within the c'tor and call Exit if you can't construct the class properly. 如果无法正确构造类,则必须处理c'tor中的所有异常,并调用Exit。 It doesn't make sense for an instance to be locked--which this essentially means by locking the c'tor. 锁定一个实例没有意义-这本质上就是通过锁定c'tor来实现的。 You might want to look at the Synchronized attribute; 您可能需要查看Synchronized属性; but I don't think that's really recommended. 但我不建议您这样做。

Is it critical that requests from other objects upon your static object be executed immediately? 立即执行来自其他对象的静态对象的请求是否至关重要? You can achieve mutual exclusion through thread isolation if you have your static object maintain a queue that it itself works through. 如果让静态对象维护一个队列,该队列本身可以通过它进行工作,则可以通过线程隔离来实现互斥。 On a call from another object, the requested work is placed on the queue, while in a separate thread the static object is working through the queue (note the need for mutually exclusive access to the queue, though!) performing requests. 在从另一个对象进行调用时,所请求的工作被放置在队列中,而在单独的线程中,静态对象正在通过队列(请注意,需要互斥访问队列!)来执行请求。

You can either have the calling objects block in the method that added the work to the queue until notified by the static object, or provide a callback interface to allow the static object to notify the calling objects that their work is done. 您可以在将工作添加到队列中直到静态对象通知之前的方法中,具有调用对象块,或者提供一个回调接口以允许静态对象通知调用对象其工作已完成。

From your example is not exactly clear what you're trying to do. 从您的示例中并不清楚您要做什么。 As good programming practice it is better to have each individual method obtain the lock and release it when it is done with the critical section. 作为良好的编程习惯,最好让每个单独的方法都获得关键,然后在关键部分完成后释放它。 In your case it will be: 在您的情况下,它将是:

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
}

Now lock is a short-cut version for using the Monitor class. 现在锁是使用Monitor类的快捷方式。 And is actually translated like: 实际上是这样翻译的:

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

This will give you more control over the state of your object & internal representation, as if there something wrong happens you can revert back to the previous state of your object. 这将使您能够更好地控制对象和内部表示的状态,好像发生了什么错误一样,您可以恢复到对象的先前状态。

If there are not many combinations of calls you could make dostuff and doStuff2 private and place a wrapper function in your class with a lock 如果调用的组合不多,则可以将dostuff和doStuff2设为私有,并使用锁将包装函数放置在类中

static public void doStuffs()
{
    lock (lockObject)
    {
        doStuff();
        doStuff2();
    }
}

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

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