Exploring the multi-thread mechanism of c #

Source: Internet
Author: User
Expression indicates the object you want to trace. It is usually an object reference. Generally, if you want to protect an instance of a class, you can use this; if you want to protect a static variable (such as mutual exclusion) Code In a static method. The statement_block is the code of the mutex, which can be executed by only one thread at a time.

The following is a typical example of using the lock keyword. I will explain the usage and usage of the lock keyword in the comment:

// Lock. CS
Using system;
Using system. Threading;

Internal class account
{
Int balance;
Random r = new random ();
Internal account (INT initial)
{
Balance = initial;
}

internal int withdraw (INT amount)
{< br> If (balance <0)
{< br> file: // if the balance is smaller than 0, an exception is thrown.
throw new exception ("negative balance ");
}< br> // The following Code ensures that the balance value is modified before the current thread modifies the balance value
// no other threads execute this code to modify the balance value.
// Therefore, the balance value cannot be less than 0
lock (this)
{< br> console. writeline ("current thread:" + thread. currentthread. name);
file: // If the lock keyword is not protected, the file may be executed after the if condition is determined.
file: // another thread executes the balance = balance-amount command to modify the balance value
file: // and this modification is invisible to this thread, therefore, the if condition is no longer true.
file: // However, the thread continues to execute balance = balance-amount, therefore, the balance may be less than 0
If (balance> = Amount)
{< br> thread. sleep (5);
balance = balance-amount;
return amount;
}< br> else
{< br> return 0; // Transaction rejected
}< BR >}< br> internal void dotransactions ()
{< br> for (INT I = 0; I <100; I ++)
withdraw (R. next (-50,100);
}< BR >}

Internal class test
{
Static internal thread [] threads = new thread [10];
Public static void main ()
{
Account ACC = new account (0 );
For (INT I = 0; I <10; I ++)
{
Thread t = new thread (New threadstart (Acc. dotransactions ));
Threads [I] = T;
}
For (INT I = 0; I <10; I ++)
Threads [I]. Name = I. tostring ();
For (INT I = 0; I <10; I ++)
Threads [I]. Start ();
Console. Readline ();
}
}

When multiple threads share an object, there will also be a problem similar to the Public Code. In this case, you should not use the lock keyword. here you need to use system. A kind of Monitor In threading is called a monitor, which provides a solution to share resources with threads.

The monitor class can lock an object. A thread can operate on this object only when this lock is obtained. The object lock mechanism ensures that only one thread can access this object at a time point that may cause confusion. Monitor must be associated with a specific object, but because it is a static class, it cannot be used to define the object, and all its methods are static, objects cannot be referenced. The following code uses monitor to lock an object:

......
Queue oqueue = new Queue ();
......
Monitor. Enter (oqueue );
... // Now the oqueue object can only be manipulated by the current thread
Monitor. Exit (oqueue); // release the lock

As shown above, when a thread calls monitor. when the enter () method locks an object, this object will be owned by it. Other threads want to access this object, only waiting for it to use monitor. the exit () method releases the lock. To ensure that the thread can release the lock in the end, you can write the monitor. Exit () method into the finally code block in the try-catch-Finally structure. For any monitor-locked object, some information related to it is stored in the memory. One is the reference of the thread currently holding the lock, and the other is a reserve queue column, the queue stores the thread that is ready to obtain the lock. The third is a waiting queue, which stores the reference of the queue that is currently waiting for this object to change its status. When the thread that owns the object lock is about to release the lock, it uses monitor. the pulse () method notifies the first thread in the waiting queue, so the thread is transferred to the reserve queue column. When the object lock is released, the thread in the reserve queue column can immediately obtain the object lock.

The following is an example of how to use the lock keyword and the monitor class to synchronize and communicate threads. It is also a typical producer and consumer problem. In this routine, the producer thread and consumer thread run alternately. When the producer writes a number, the consumer immediately reads and displays it. I will introduce this in the comment.Program. The system namespace used is as follows:

Using system;
Using system. Threading;

First, we define the cell class of the object to be operated. In this class, there are two methods: readfromcell () and writetocell. The consumer thread will call readfromcell () to read and display the cellcontents content. The producer process will call the writetocell () method to write data to cellcontents.

Public class Cell
{
Int cellcontents; // content in the cell object
Bool readerflag = false; // status flag. It can be read if it is true. If it is false, it is being written.
Public int readfromcell ()
{
Lock (this) // What is guaranteed by the lock keyword? Please refer to the previous introduction to lock.
{
If (! Readerflag) // if it cannot be read now
{
Try
{
File: // wait for the writetocell method to call the monitor. Pulse () method.
Monitor. Wait (this );
}
Catch (synchronizationlockexception E)
{
Console. writeline (E );
}
Catch (threadinterruptedexception E)
{
Console. writeline (E );
}
}
Console. writeline ("consume: {0}", cellcontents );
Readerflag = false; file: // resets the readerflag, indicating that the consumption has been completed.
Monitor. Pulse (this); file: // notify the writetocell () method (this method is executed in another thread, waiting)
}
Return cellcontents;
}

Public void writetocell (int n)
{
Lock (this)
{
If (readerflag)
{
Try
{
Monitor. Wait (this );
}
Catch (synchronizationlockexception E)
{
File: // when the synchronous method (the method of the monitor class except enter) is called in the non-Synchronous Code Area
Console. writeline (E );
}
Catch (threadinterruptedexception E)
{
File: // stop when the thread is waiting
Console. writeline (E );
}
}
Cellcontents = N;
Console. writeline ("produce: {0}", cellcontents );
Readerflag = true;
Monitor. Pulse (this); file: // notify another thread of the readfromcell () method being waited
}
}
}

The following defines the producer cellprod and consumer class cellcons. They all have only one method threadrun (), so that the threadstart proxy object provided to the thread in the main () function is used as the thread entry.

public class cellprod
{< br> cell; // The cell object to be operated
int quantity = 1; // number of producer productions, initialized to 1

Public cellprod (cell box, int request)
{< br> // constructor
cell = box;
quantity = request;
}< br> Public void threadrun ()
{< br> for (INT loty = 1; loty <= quantity; loty ++)
cell. writetocell (logoff); file: // The producer writes information to the operation object
}< BR >}

public class cellcons
{< br> cell;
int quantity = 1;

Public cellcons (cell box, int request)
{< br> cell = box;
quantity = request;
}< br> Public void threadrun ()
{< br> int valreturned;
for (INT loned = 1; looper <= quantity; loty ++)
valreturned = cell. readfromcell (); // The Consumer reads information from the operation object
}< BR >}

In the main () function of the monitorsample class below, we need to create two threads as producers and consumers respectively and use cellprod. threadrun () method and cellcons. the threadrun () method operates on the same cell object.

Public class monitorsample
{
Public static void main (string [] ARGs)
{
Int result = 0; file: // a flag. If it is 0, no program error occurs. If it is 1, an error occurs.
Cell cell = new cell ();

// Use cell to initialize the cellprod and cellcons classes. The number of times of production and consumption is 20.
Cellprod prod = new cellprod (cell, 20 );
Cellcons cons = new cellcons (cell, 20 );

Thread producer = new thread (New threadstart (prod. threadrun ));
Thread consumer = new thread (New threadstart (cons. threadrun ));
// Producer thread and consumer thread have been created, but execution has not started

Try
{
Producer. Start ();
Consumer. Start ();

producer. join ();
consumer. join ();
console. readline ();
}< br> catch (threadstateexception e)
{< br> file: // when the thread cannot perform the requested operation because of its status
console. writeline (E);
result = 1;
}< br> catch (threadinterruptedexception e)
{< br> file: // abort when the thread is waiting
console. writeline (E);
result = 1;
}< br> // although the main () function does not return a value, however, the following statement returns the execution result to the parent process
environment. exitcode = result;
}< BR >}

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.