[Linux application programming] POSIX thread mutual exclusion and synchronization mechanism-spin lock



1 Introduction

  The previous article separately described the meaning, attributes, usage principles, usage scenarios, and usage methods of mutex locks and read-write locks. This article describes the third type of lock besides mutual exclusion lock and read-write lock- spin lock .


2 Spin lock

  Spin lock is a mechanism of mutual exclusion between threads. A spin lock is essentially a lock, and its function is exactly the same as that of a mutual exclusion lock. Only one thread is allowed to hold the lock at any time to achieve the purpose of mutually exclusive access to shared resources. The only difference is that the scheduling strategies of the two are different. When the thread cannot apply for the mutex lock, the thread sleeps and gives up cpu resources. After the mutex is obtained, the thread wakes up and continues execution; while the spin lock will not be blocked. Cause the thread to sleep and occupy cpu resources until the spin lock is obtained. Spin lock is a kind of lightweight lock. Compared with mutual exclusion lock, the resource overhead is smaller. Spin lock is the most ideal choice for locking in a very short time and can improve efficiency.


2.1 Spin lock features

  The characteristics of the spin lock match its naming. When the thread cannot acquire the lock, it is always in a busy waiting (spinning in place?) state, and it cannot handle any tasks while occupying the CPU. According to the characteristics of the spin lock, the spin lock is suitable for scenes where the lock time is extremely short, and long-term use of the spin lock will reduce the system performance. If accessing resources is time-consuming and requires a long time to hold the lock, you need to consider other mutual exclusion mechanisms.

  • For thread mutual exclusion
  • Blocking keeps occupying cpu resources
  • Can not cause thread to sleep
  • Lightweight lock
  • Low resource overhead, including creation, holding, and release process

2.2 Application scenarios of spin lock

   Spin locks were originally introduced as a mutual exclusion mechanism to prevent multi-core processor (SMP) concurrency from causing race conditions. Spin locks are rarely used in user mode. In kernel mode, spin locks are often used in common driver development. For the use of spin locks in kernel mode, please refer to the article Concurrency and Race (how to choose a suitable protection mechanism) . Spin locks are suitable for lightweight locking in a short period of time.

  • Mutex resource access time is very short (short lock time), less than the time of 2 context switches
  • Special scenarios, do not want to hang threads

2.3 Principles of using spin lock

  Spin locks are the same as mutexes. The principle of using spin locks can refer to the principle of using mutexes. The principle of using mutexes is also the basic principle of using spin locks.

  • The lock time is extremely short, and the lock is released in time
  • Do not allow nested (recursive) applications to hold spin locks, otherwise it will cause deadlock
  • Avoid excessive spin lock applications and prevent waste of cpu resources

Note:

When applying for holding a spin lock, it will always occupy the CPU. If a spin lock is applied for nested or recursively, when the lock is applied for at the second level, because the lock is held by the first level, the second level cannot obtain the lock and is always in a waiting state And occupy the cpu, the program can not jump to the outermost layer to release the lock, resulting in deadlock. Therefore, use spin locks in recursive programs with caution


3 Spin lock use

  The basic steps for spin lock use are:

[1] Create a spin lock instance

[2] Initialize the spin lock

[3] Hold a spin lock

[4] Release the spin lock

[5] Destroy the spin lock example


3.1 Create a spin lock

  The posix thread spin lock pthread_spinlock_tis represented by a data structure. Spinlock instances can be created statically and dynamically.

pthread_spinlock_t spinlock;

3.2 Initialize the spin lock

  Spinlock initialization only supports pthread_rwlock_initdynamic initialization using functions.

int pthread_spin_init(pthread_spinlock_t *spinlock, int pshared);
  • spinlock, Spinlock instance address, cannot be NULL

  • pshared, Spinlock scope

    PTHREAD_PROCESS_PRIVATE, In-process (creator) scope, can only be used for mutual exclusion of in-process threads

    PTHREAD_PROCESS_SHARED, Cross-process scope, used for mutual exclusion between all threads of the system

  • Return, return 0 if successful, return EINVAL if the parameter is invalid


3.3 Spin lock lock (application lock)

  Spin lock application holding is divided into blocking mode and non-blocking mode. The commonly used mode is generally the blocking mode.


3.3.1 Blocking method

int pthread_spin_lock(pthread_spinlock_t *spinlock);
  • spinlock, Spinlock instance address, cannot be NULL

  • Return, return 0 if successful, return EINVAL if the parameter is invalid

  If the spin lock has not been held (locked) by other threads, the thread applying for the spin lock obtains the lock. If the spin lock is held by other threads, the thread has been in a waiting state (occupying the cpu) until the thread holding the spin lock is unlocked, and the thread obtains the lock to continue execution. Recursive nesting of spin locks is not allowed, otherwise it will cause deadlock.


3.3.2 Non-blocking mode

int pthread_spin_trylock(pthread_spinlock_t spinlock*);
  • spinlock, Spinlock instance address, cannot be NULL
  • return
return value description
0 success
EINVAL Invalid argument
EDEADLK Deadlock
EBUSY The lock is held by another thread

  Calling this function will return immediately without blocking waiting. Actual applications can perform different task operations based on the return status.


3.4 Spin lock release

int pthread_spin_unlock(pthread_spinlock_t *spinlock);
  • spinlock, Spinlock instance address, cannot be NULL
  • return
return value description
0 success
EINVAL Invalid argument
EDEADLK Deadlock
EBUSY The lock is held by another thread

  The spin lock must be released in time after being held, and the lock must not be released multiple times.


3.5 Spin lock destruction

int pthread_spinlock_destroy(pthread_spinlock_t *spinlock);
  • spinlock, Spinlock instance address, cannot be NULL
  • return
return value description
0 success
EINVAL The spinlock has been destroyed, or the spinlock is empty
EBUSY Spin lock is used by other threads

  pthread_spinlock_destroyUsed to destroy a spinlock that has been initialized dynamically. The destroyed spin lock is in an uninitialized state, and the spin lock attributes and control block parameters are in an unusable state. There are several points to note when using the destroy function:

  • Destroyed spin locks can be pthread_spinlock_initre-initialized
  • Can not repeatedly destroy the destroyed spin lock
  • Can only be destroyed when no thread holds the spin lock

3.6 Write an example

  Code realization function:

  • Create two threads
  • Two threads respectively access global variables and output to the terminal
  • Expected result, thread 1 output result "1 2 3 4 5", thread 2 output result "5 4 3 2 1"
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include "pthread.h" 

#define	USE_SPINLOCK 1	/* 是否使用自旋锁,使用,0不使用 */

#if USE_SPINLOCK
pthread_spinlock_t spinlock;
#endif

static int8_t g_count = 0;

void *thread0_entry(void *data)  
{
    
    
	uint8_t  i =0;

#if USE_SPINLOCK
	pthread_spin_lock(&spinlock);
#endif
	for (i = 0;i < 5;i++)
	{
    
    
		g_count ++;
		printf("%d ", g_count);
		usleep(100);
	}
	printf("\r\n");
#if USE_SPINLOCK
	pthread_spin_unlock(&spinlock);
#endif
}

void *thread1_entry(void *data)  
{
    
    
	uint8_t  i =0;

	usleep(10);	/* 让线程0先执行 */
#if USE_SPINLOCK
	pthread_spin_lock(&spinlock);
#endif
	for (i = 0;i < 5;i++)
	{
    
    
		printf("%d ", g_count);
		g_count--;
		usleep(100);
	}
	printf("\r\n");
#if USE_SPINLOCK
	pthread_spin_unlock(&spinlock);
#endif
}

int main(int argc, char **argv)  
{
    
    
	pthread_t thread0,thread1; 
    void *retval; 
    
#if USE_SPINLOCK
	pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);/* 进程内作用域 */
#endif
    pthread_create(&thread0, NULL, thread0_entry, NULL);
	pthread_create(&thread1, NULL, thread1_entry, NULL);
    pthread_join(thread0, &retval);
    pthread_join(thread1, &retval);
	
	return 0;
 }

Results without spin lock

  Because no locks are used, threads execute concurrently, and "at the same time" access global variables g_countand printfoutputs, the actual results did not meet expectations.

acuity@ubuntu:/mnt/hgfs/LSW/STHB/pthreads/spinlock$ gcc spinlock.c -o spinlock -lpthread
acuity@ubuntu:/mnt/hgfs/LSW/STHB/pthreads/spinlock$ ./spinlock
1 2 2 2 2 2 2 1 1 1

Results of using spin lock

  After thread 0 holds the lock, the lock is released after the access is executed. Thread 2 applies for the lock and the output result is correct.

acuity@ubuntu:/mnt/hgfs/LSW/STHB/pthreads/spinlock$ gcc spinlock.c -o spinlock -lpthread
acuity@ubuntu:/mnt/hgfs/LSW/STHB/pthreads/spinlock$ ./spinlock
1 2 3 4 5 
5 4 3 2 1 

  In the code, the printffunction is locked, and actual use is not allowed, which violates the principle of locking. This is just a simulation scenario test.


4 Spinlock properties

  A spin lock is a lightweight lock with only one "scope" for its attributes pthread_spin_init. The scope is specified when the function is called to initialize the spin lock. Spin lock scope represents the mutual exclusion scope of spin lock, which is divided into in-process (creator) scope PTHREAD_PROCESS_PRIVATEand cross-process scope PTHREAD_PROCESS_SHARED. In-process scope can only be used for mutual exclusion of in-process threads, and cross-process can be used for mutual exclusion among all threads of the system.


5 Summary

  The functions implemented by spin locks are the same as mutex locks, which are used for mutual exclusion access between threads. A spin lock is a lightweight lock that does not cause thread sleep. It is suitable for scenarios with a very short lock time. Because its resource overhead is lower than that of a mutex, the efficiency of using spin locks in a very short lock scenario will be higher. The precautions for the use of spin locks, combined with the "Principles of Using Mutex Locks" in Section 2.3 of the Mutex article , refer to the "Principles of Using Spin Locks" in Section 2.3. So far, the description of mutual exclusion lock, read-write lock, and spin lock is complete. The differences in the characteristics of the three are listed below for comparison.

Comparison of mutual exclusion locks, read-write locks, and spin locks

main feature Cause thread sleep Scope of application Resource cost
Mutex Mutually exclusive Yes General exclusive access ordinary
Read-write lock Read sharing Yes Read more and write less ordinary
Spin lock Spin waiting no Very short lock time Low overhead

Guess you like

Origin blog.csdn.net/qq_20553613/article/details/106323144