C # multi-thread programming examples

Source: Internet
Author: User

A single Write Program/multiple read programs have been provided in the. Net class library, that is, the System. Threading. ReaderWriterLock class. This article explores multi-thread programming in c # by analyzing a common single write/multiple read programs.

Question proposal

The thread synchronization problem of a single write program or multiple read programs refers to the need to modify shared resources when any number of threads access shared resources, the reading program (thread) needs to read data. In this synchronization problem, it is easy to obtain the following two requirements:

1) when a thread is writing data, other threads cannot write or read data.

2) When a thread is reading data, other threads cannot write but can read data.

This problem is often encountered in the database application environment. For example, there are n end users who need to access the same database at the same time. M users need to store data into the database, and n-m users need to read records in the database.

Obviously, in this environment, we cannot allow two or more users to update the same record at the same time. If two or more users attempt to modify the same record at the same time, the information in the record will be damaged.

We do not allow a user to update database records, while allowing another user to read the records. Because the read record may contain both updated and unupdated information, this record is invalid.

Implementation Analysis

Requires that any thread must apply for a lock before writing or reading resources. Read locks and write locks are divided according to different operations. After the operation is completed, the corresponding locks should be released. You can change the requirements of a single write program or multiple read programs in the following format:

A successful condition for a thread to apply for a read lock is that there is no active write thread currently.

A successful condition for a thread to apply for a write lock is that there is currently no active (for the lock) thread.

Therefore, a variable m_nActive is introduced to indicate whether there is an active thread and whether it is a writing or reading thread. If m_nActive> 0, it indicates the number of reading threads of the current active activity, if m_nActive = 0, it indicates that there are no active threads, m_nActive <0, indicating that there are currently write threads active. Note that m_nActive <0, only the value of-1 can be obtained, because only one write thread is allowed.

To determine the type of the lock owned by the active thread, we use the Thread Local Storage Technology (see other reference books) to associate the thread with the special flag.

The prototype of the function used to apply for the read lock is public void AcquireReaderLock (int millisecondsTimeout). The parameter is the waiting time of the thread. The function is defined as follows:

Public void AcquireReaderLock (int millisecondsTimeout)

{

// M_mutext can be obtained quickly to enter the critical section.

M_mutex.WaitOne ();

// Whether a write thread exists

Bool bExistingWriter = (m_nActive <0 );

If (bExistingWriter)

{File: // etc.The number of threads to be read increases by 1. When a lock is released, the thread is scheduled based on the number.

M_nWaitingReaders ++;

}

Else

{File: // whenAdd 1 to the previous active thread

M_nActive ++;

}

M_mutex.ReleaseMutex ();

// The storage lock flag is Reader

System. LocalDataStoreSlot slot = Thread. GetNamedDataSlot (m_strThreadSlotName );

Object obj = Thread. GetData (slot );

LockFlags flag = LockFlags. None;

If (obj! = Null)

Flag = (LockFlags) obj;

If (flag = LockFlags. None)

{

Thread. SetData (slot, LockFlags. Reader );

}

Else

{

Thread. SetData (slot, (LockFlags) (int) flag | (int) LockFlags. Reader ));

}


If (bExistingWriter)

{File: // etc.Time to be specified

This. m_aeReaders.WaitOne (millisecondsTimeout, true );

}

}

It first enters the critical section (used to ensure the correctness of the number of active threads in a multi-threaded environment) to determine the number of active threads. If a write thread (m_nActive <0) exists, the number of reading threads waiting for the specified time plus 1. If the current active thread is a read thread (m_nActive> = 0), the read thread can continue to run.


The prototype of the function for applying for the write lock is public void AcquireWriterLock (int millisecondsTimeout). The parameter is the waiting time. The function is defined as follows:

Public void AcquireWriterLock (int millisecondsTimeout)

{

// M_mutext can be obtained quickly to enter the critical section.

M_mutex.WaitOne ();

// Whether an active thread exists

Bool bNoActive = m_nActive = 0;

If (! BNoActive)

{

M_nWaitingWriters ++;

}

Else

{

M_nActive --;

}

M_mutex.ReleaseMutex ();

// Stores the thread lock mark

System. LocalDataStoreSlot slot = Thread. GetNamedDataSlot ("myReaderWriterLockDataSlot ");

Object obj = Thread. GetData (slot );

LockFlags flag = LockFlags. None;

If (obj! = Null)

Flag = (LockFlags) Thread. GetData (slot );

If (flag = LockFlags. None)

{

Thread. SetData (slot, LockFlags. Writer );

}

Else

{

Thread. SetData (slot, (LockFlags) (int) flag | (int) LockFlags. Writer ));

}

// If there is an active thread, wait for the specified time

If (! BNoActive)

This. m_aeWriters.WaitOne (millisecondsTimeout, true );

}

It first enters the critical section to determine the number of active threads. If an active thread exists, whether it is a write thread or a read thread (m_nActive ), the thread adds 1 to the number of write threads waiting for the specified time. Otherwise, the thread has the write permission.

The prototype of the function for releasing the read lock is public void ReleaseReaderLock (). The function is defined as follows:

Public void ReleaseReaderLock ()

{

System. LocalDataStoreSlot slot = Thread. GetNamedDataSlot (m_strThreadSlotName );

LockFlags flag = (LockFlags) Thread. GetData (slot );

If (flag = LockFlags. None)

{
Return;

}

Bool bReader = true;

Switch (flag)

{

Case LockFlags. None:

Break;

Case LockFlags. Writer:

BReader = false;

Break;

}

If (! BReader)

Return;

Thread. SetData (slot, LockFlags. None );

M_mutex.WaitOne ();

AutoResetEvent autoresetevent = null;

This. m_nActive --;

If (this. m_nActive = 0)

{

If (this. m_nWaitingReaders> 0)

{

M_nActive ++;

M_nWaitingReaders --;

Autoresetevent = this. m_aeReaders;

}

Else if (this. m_nWaitingWriters> 0)

{

M_nWaitingWriters --;

M_nActive --;

Autoresetevent = this. m_aeWriters;

}

}

M_mutex.ReleaseMutex ();

If (autoresetevent! = Null)

Autoresetevent. Set ();

}

When releasing the read lock, first determine whether the current thread has the read lock (through the mark of local storage of the thread), and then determine whether there is a waiting read thread. If yes, first add 1 to the current active thread, wait for the number of reading threads to be reduced by 1, and then set the event to a signal. If there is no waiting reading thread, check whether there is a waiting writing thread. If so, the number of active threads is reduced by 1 and the number of waiting writing threads is reduced by 1. The process of releasing the write lock is basically the same as that of releasing the read lock. See the source code.

Note that when the lock is released in the program, only one reading program will be awakened. This is because the original calendar of the AutoResetEvent can be changed to ManualResetEvent by yourself and multiple reading programs can be awakened at the same time, in this case, m_nActive should be equal to the total number of waiting reading threads.


Test

The test program is taken from an example in. Net FrameSDK and is only slightly modified. The test procedure is as follows,

Using System;

Using System. Threading;

Using MyThreading;

Class Resource {

MyReaderWriterLock rwl = new myReaderWriterLock ();

Public void Read Int32 (threadNum ){

Rwl. AcquireReaderLock (Timeout. Infinite );

Try {

Console. WriteLine ("Start Resource reading (Thread = {0})", threadNum );

Thread. Sleep (250 );

Console. WriteLine ("Stop Resource reading (Thread = {0})", threadNum );

}

Related Article

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.