Article Directory
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_t
is 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_init
dynamic initialization using functions.
int pthread_spin_init(pthread_spinlock_t *spinlock, int pshared);
-
spinlock
, Spinlock instance address, cannot be NULL -
pshared
, Spinlock scopePTHREAD_PROCESS_PRIVATE
, In-process (creator) scope, can only be used for mutual exclusion of in-process threadsPTHREAD_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_destroy
Used 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_init
re-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_count
and printf
outputs, 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 printf
function 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_PRIVATE
and 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 |