Multithreading-Thread Sync Lock (lock, Monitor)

Source: Internet
Author: User
Tags lock queue

1. Preface

Multithreaded programming, we not only want to be able to achieve a logical sequencing between two threads, but also want to two unrelated threads when accessing the same resource, while only one thread can operate on the resources, otherwise there will be unpredictable results.

For example, there are two threads need to add 1 to the same counter, we want the result is the counter finally add 2, but may also get to this counter, the first thread to the counter plus 1, but the second thread does not know, and then re-add 1 to the counter, resulting in the loss of a count of the final counter. To solve this problem, it must be locked before the counter is fetched, preventing other threads from acquiring it again until the processing is complete and then released.

Monitor, lock is introduced to deal with this kind of problem.

2. Grammatical Sugars

Before we talk about monitor, lock, let's start with a simple concept-what is syntactic sugar? In fact, the syntax of sugar is such a kind of programming, to simplify the coding statements, improve coding efficiency.

For example: we declare properties

private string M_name;public string name{    get {return m_name;}    set {m_name = value;}}

If we define a lot of this attribute in a class, then we have to repeat the same code, it doesn't make any sense, so C # gives us another easy way to write:

public string name{get; set;}

Very simple! The read-write operation of the property name is still provided, but the actual code is given to the C # compiler to help us complete, and the resulting code is exactly the same. So the second form is the first form of syntactic sugar.

In fact, the first form of writing is also a syntactic sugar, which is actually a simplification of methods Get_name and Set_name (see the explanation of property accessors in csdn for details).

Lock is also a syntactic sugar, which is the syntax sugar of Monitor (Monitor.Enter and Monitor.Exit), and the following code is equivalent:

Lock (obj) {...}
try{    monitor.enter (obj);    ....} finally{    monitor.exit (obj);}

3. Can lock completely replace monitor

The existence is meaningful, of course not completely replaced, because monitor not only provides the Monitor.Enter and Monitor.Exit methods, but also provides a method similar to signal manipulation for thread synchronization monitor.wait and Monitor.pulse.

In most cases, when we use Monitor.Enter and Monitor.Exit only for resource synchronization, we can replace it with lock and make the code easier to understand. But if we want to have more precise control over the locked code, we need to use the Monitor method, the following code:

Queue m_tokenqueue = new Queue (), ... lock (obj) {    if (M_tokenqueue.count > 0)    {        var data = m_tokenqueue.dequ Eue ();    }    else    {        thread.sleep (60000);    }}

When we want to operate the queue m_tokenqueue, we need to lock the resources before we can determine when the queue has data, otherwise wait a minute. There is a problem here, when we find that there is no data in the queue, we want to release the lock resource immediately, not until a minute later, so that in this minute, even if other threads want to access the queue will wait a minute, and this wait is completely meaningless!

In this moment, you need to use the notation of the monitor

Monitor.Enter (obj); if (M_tokenqueue.count > 0) {    var data = M_tokenqueue.dequeue ();    Monitor.Exit (obj);} else{    monitor.exit (obj);    Thread.Sleep (60000);}

This is the decision to release the lock immediately after completion, the other threads will not have to wait for sleep and then get the resource lock! The price, however, is to add the Monitor.Exit method to each judgment branch to ensure that the resource lock is released.

This detail is often used in my project and has not yet found a better alternative.

4. What are lock locks?

Since it is a lock synchronization resource, we take it for granted that the parameter should be the object to be locked, as in the lock queue above, the code should look like this:

Queue M_tokenqueue = new Queue (); Lock (M_tokenqueue) {...}

This looks easy to understand. That's fine, but we'll come back to discuss the question.

In fact, we should understand lock as locking a piece of code, and this code to manipulate the boundary resources! Lock (Object obj) determines whether the lock is synchronous by checking that obj is the same object, that is, whatever this obj is, as long as it is the same object (that is, checking if it is the same address)!

Then, come back to see what you just said, lock (M_tokenqueue) is obviously the right way, but there is no guarantee that the M_tokenqueue object address is not changed, because the related thread is the operation of this resource, once a thread to the queue re-assignment, will cause other synchronization failure! The following code:

class program{static Queue m_tokenqueue = new Queue ();        static void Main (string[] args) {ThreadPool.QueueUserWorkItem (new WaitCallback (METHOD1), null);        ThreadPool.QueueUserWorkItem (New WaitCallback (METHOD2), null);    Console.readkey (); } private static void Method1 (Object obj) {lock (M_tokenqueue) {Console.WriteLine (Datetim            E.now.tostring ("Mm:ss") + ", enter 1");            M_tokenqueue = new Queue ();        Thread.Sleep (10000);    } Console.WriteLine (DateTime.Now.ToString ("mm:ss") + ", exit 1");        } private static void Method2 (Object obj) {thread.sleep (1000);        Lock (M_tokenqueue) {Console.WriteLine (DateTime.Now.ToString ("mm:ss") + ", enter 2");    } Console.WriteLine (DateTime.Now.ToString ("mm:ss") + ", exit 2"); }}

Start thread 1, lock for m_tokenqueue,10 seconds, and start thread 2, hibernate for 1 seconds, and ensure that the threads lock resources and ensure that they are executed to Thread.Sleep (10000) before entering lock. Ideally, you want thread 1 to be unlocked, thread 2 to output, but thread 1 does such an operation M_tokenqueue = new Queue (); At this time, the M_tokenqueue address has changed, so the two thread lock (m_tokenqueue) is different, thread 2 will not wait for direct execution, the result is as follows:

In summary, the use of Lock (M_tokenqueue) is obviously not a safe and efficient way, since you want to lock the object address is immutable, then you can set a read-only object such as

Private ReadOnly Object obj = new Object (); As lock object, the code is guaranteed to execute safely.

5. SyncRoot

As a matter of fact. NET in some collection classes (such as Hashtable, Queue, ArrayList, etc.), has provided us with such a lock for the use of object SyncRoot (defined in the interface ICollection), so the above when we synchronize the resources, It can be written like this:

Queue M_tokenqueue = new Queue (); Lock (m_tokenqueue.syncroot) {    ...}

In the case of a generic queue, a cast is required:

queue<int> m_tokenqueue = new queue<int> (); Lock (((ICollection) m_tokenqueue). SyncRoot) {    ...}

Multithreading-Thread Sync Lock (lock, Monitor)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.