当前位置:网站首页>15. several methods of thread synchronization
15. several methods of thread synchronization
2022-06-25 01:13:00 【anieoo】
One 、 Why do I need thread synchronization
Thread synchronization is usually a problem that occurs in a multithreaded environment , For variables in shared memory accessed by multiple threads at the same time , If not protected , It will cause some column data error problems . The following is an example :

Assuming that thread A The value of the variable read for the first time is 10, Each write cycle will change the variable A Add 5, Theoretically, when a thread A When he finishes his task , The value of the variable becomes 20, But because of the thread B Is a variable read during two write cycles , The result is 15, This will cause data errors .
Two 、 The mutex
The mutex , Is a way to synchronize threads . seeing the name of a thing one thinks of its function , When a thread A When accessing shared memory , Lock it , Unlock at the end of the access . During locking , If the thread B If you want to apply for access to shared memory resources , Will be blocked , Until the thread A Release the mutex .
1、 Mutex initialization
pthread_mutex_init() function
Initialize mutex , Its function prototype is as follows :
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
To use this function, you need to include a header file <pthread.h>.
mutex: Parameters mutex It's a pthread_mutex_t Type a pointer , Point to the mutex object that needs to be initialized ;
attr: Parameters attr It's a pthread_mutexattr_t Type a pointer , Point to one pthread_mutexattr_t Type object , This object defines the properties of the mutex
Return value : Successfully returns 0; Failure will return a non - 0 Error code Call function pthread_mutex_lock() Mutex locks can be locked
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);And calling functions pthread_mutex_unlock() The mutex can be unlocked
int pthread_mutex_unlock(pthread_mutex_t *mutex);You need to destroy mutexes when they are not needed :
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
You cannot destroy a mutex that has not been unlocked , Otherwise, there will be a mistake ;
Mutexes that are not initialized cannot be destroyed Test code :
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
static pthread_mutex_t mutex; // Define spin lock
static int count = 0;
static int loops;
static void *new_pthread(void *arg)
{
int loops = *((int *)arg);
int l_count, j;
for (j = 0; j < loops; j++) {
//pthread_mutex_lock(&mutex); // Lock the mutex
l_count = count;
l_count++;
count = l_count;
//pthread_mutex_unlock(&mutex);// Mutex unlock
}
return (void *)0;
}
int main(int argc, char *argv[])
{
pthread_t tid1, tid2; // Define two threads
int ret;
loops = atoi(argv[1]); // Number of save cycles
/* Initialize mutex */
//pthread_mutex_init(&mutex, NULL);
// Create the first thread
ret = pthread_create(&tid1, NULL, new_pthread, &loops);
if(ret) {
fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
exit(-1);
}
// Create a second thread
ret = pthread_create(&tid2, NULL, new_pthread, &loops);
if (ret) {
fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
exit(-1);
}
/* Wait for the thread to end */
ret = pthread_join(tid1, NULL);
if (ret) {
fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
exit(-1);
}
ret = pthread_join(tid2, NULL);
if (ret) {
fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
exit(-1);
}
/* Print the results */
printf("count = %d\n", count);
pthread_mutex_destroy(&mutex);
exit(0);
}Create two threads for variables count add loops frequency , Protect variables with mutexes count, Prevent data reading errors . The operation results are as follows :

If you annotate the mutex point , It can be found that the running results are as follows , Can lead to inconsistent data :

pthread_mutex_trylock() function
In a thread A During holding the mutex , If the thread B call pthread_mutex_lock() Function to get the mutex , Will continue to block , Until the thread A Release .
For situations where you don't want to be blocked ,Linux Provides pthread_mutex_trylock() function , The function prototype is as follows :
#include <pthread.h>
int pthread_mutex_trylock(pthread_mutex_t *mutex);
Parameters mutex Point to target mutex , Successfully returns 0, Failure returns a non - 0 Error code of value , If the target mutex has been locked by another
Thread lock , The call fails and returns EBUSY.The function test is as follows :
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
static pthread_mutex_t mutex;
static int count = 0;
static int loops;
static void *new_pthread(void *arg)
{
int loops = *((int *)arg);
int l_count, j;
for (j = 0; j < loops; j++) {
while(pthread_mutex_trylock(&mutex)); // Lock in a non blocking manner
l_count = count;
l_count++;
count = l_count;
pthread_mutex_unlock(&mutex);// Mutex unlock
}
return (void *)0;
}
int main(int argc, char *argv[])
{
pthread_t tid1, tid2; // Define two threads
int ret;
loops = atoi(argv[1]); // Number of save cycles
/* Initialize mutex */
//pthread_mutex_init(&mutex, NULL);
// Create the first thread
ret = pthread_create(&tid1, NULL, new_pthread, &loops);
if(ret) {
fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
exit(-1);
}
// Create a second thread
ret = pthread_create(&tid2, NULL, new_pthread, &loops);
if (ret) {
fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
exit(-1);
}
/* Wait for the thread to end */
ret = pthread_join(tid1, NULL);
if (ret) {
fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
exit(-1);
}
ret = pthread_join(tid2, NULL);
if (ret) {
fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
exit(-1);
}
/* Print the results */
printf("count = %d\n", count);
pthread_mutex_destroy(&mutex);
exit(0);
}The operation results are as follows , And use pthread_mutex_lock() The effect is the same :

3、 ... and 、 Condition variables,
Conditional variables are very similar to mutexes , However, conditional variables are usually used with mutexes .
Conditional variables are another synchronization mechanism available to threads . Conditional variables are used to automatically block threads , Until a specific event occurs or a condition is met . Conditional variables are used with mutexes . Using conditional variables mainly includes two actions :
① A thread is blocked waiting for a condition to be met ;
② In another thread , Issue when conditions are met “ The signal ”.
Initialization of condition variables :
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
Parameters cond Point to pthread_cond_t Conditional variable object
The function call returned 0, Failure will return a non - 0 Error code of value Notification condition variable :
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_signal() and pthread_cond_broadcast() The difference is that : Both are blocked in pthread_cond_wait()
The corresponding processing methods of multiple threads of are different , pthread_cond_signal() Function can wake up at least one thread , and
pthread_cond_broadcast() Function can wake up all threads . Wait for the condition variable :
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
cond: Point to the condition variable that needs to wait , Target condition variable ;
mutex: Parameters mutex It's a pthread_mutex_t Type a pointer , Point to a mutex object ; At the beginning, I will introduce to you
了 , Conditional variables are usually used with mutexes .
Return value : Call successfully returned 0; Failure will return a non - 0 Error code of value Test code :
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
static pthread_mutex_t mutex; // Define mutexes
static pthread_cond_t cond; // Define conditional variables
static int g_avail = 0; // Global shared resources
/* Consumer thread */
static void *consumer_thread(void *arg)
{
for ( ; ; ) {
pthread_mutex_lock(&mutex);// locked
while (0 >= g_avail)
pthread_cond_wait(&cond, &mutex);// Wait condition satisfied
while (0 < g_avail)
g_avail--; // consumption
pthread_mutex_unlock(&mutex);// Unlock
}
return (void *)0;
}
/* The main thread ( producer ) */
int main(int argc, char *argv[])
{
pthread_t tid;
int ret;
/* Initializing mutexes and conditional variables */
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
/* Create a new thread */
ret = pthread_create(&tid, NULL, consumer_thread, NULL);
if (ret) {
fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
exit(-1);
}
for ( ; ; ) {
pthread_mutex_lock(&mutex);// locked
g_avail++; // production
pthread_mutex_unlock(&mutex);// Unlock
pthread_cond_signal(&cond);// Send a signal to the condition variable
}
exit(0);
}Global variables g_avail As a shared resource between the main thread and the new thread , Two threads will lock the mutex before accessing them , Consumer thread , When it is judged that there is no product to be consumed (g_avail <= 0), call pthread_cond_wait() Put the thread into a waiting state , Wait for the condition variable , Waiting for the producer to make the product ; call pthread_cond_wait() The post thread blocks and unlocks the mutex ; In the producer thread , Its task is to produce products ( Use g_avail++ To simulate the ), After the production of the product , call pthread_mutex_unlock() Unlock the mutex , And call pthread_cond_signal() Send a signal to the condition variable ; This will wake up the consumer thread waiting for the condition variable , After waking up, the mutex is automatically acquired again , And then consume the product (g_avai-- simulation ).
Four 、 spinlocks
Spinlocks are used in much the same way as mutexes .
The difference between spinlocks and mutexes :
Differences in implementation : Mutual exclusion lock is based on spin lock , So spin lock is more basic than mutex lock ;
The difference in cost : If the mutex is not obtained, it will fall into a blocking state ( Sleep ) , Until it wakes up when it gets the lock ; And if you can't get the spin lock, you'll be in place “ The spin ”, Until you get the lock ; The overhead of sleep and wake-up is great , So the cost of mutexes is much higher than that of spinlocks 、 The efficiency of spin lock is much higher than that of mutual exclusion lock ; But if for a long time “ The spin ” wait for , Will make CPU Use efficiency is reduced , Therefore, spin lock is not applicable to the case of long waiting time .
The difference of using scene : Spin locks are rarely used in user mode applications , It is usually used more in kernel code ; Because spinlocks can be used in interrupt service functions , Mutexes don't work , When executing the interrupt service function, it is required that you cannot sleep 、 Can't be preempted ( The use of spin locks in the kernel will automatically prohibit preemption ) , Once dormancy means that when the interrupt service function is executed, it voluntarily gives up CPU Right to use , Cannot return to interrupt service function when hibernation is over , This will lead to deadlock .
Spin lock initialization
#include <pthread.h>
int pthread_spin_destroy(pthread_spinlock_t *lock);
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
Parameters lock Points to the spinlock object that needs to be initialized or destroyed
Parameters pshared Represents the process sharing attribute of the spin lock , Sure
The values are as follows :
PTHREAD_PROCESS_SHARED: Shared spin lock . The spin lock can be shared among threads in multiple processes ;
PTHREAD_PROCESS_PRIVATE: Private spin lock . Only threads in this process can use the spin lock Spin lock locking and unlocking
pthread_spin_lock() Function or pthread_spin_trylock() Function to lock the spin lock , The former is always when the lock is not acquired “ The spin ”; For the latter , If the lock cannot be acquired , It immediately returns an error , The error code is EBUSY.
#include <pthread.h>
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);5、 ... and 、 Semaphore
A semaphore is essentially a counter , It is used to read shared data objects by multiple processes ,, It's mainly used to protect shared resources ( Semaphores are also critical resources ), It makes the resource only available to one process at a time .
Semaphore related functions :
The header file :
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h> Create semaphores :
int semget(key_t key,int nsems,int flags);
(1) The first parameter key It's a long one ( The only non-zero ).
(2) The second parameter nsem Specifies the number of semaphores required in the semaphore set , Its value is almost always 1.
(3) The third parameter flag It's a set of signs , When you want to create a new semaphore when it doesn't exist , Can be flag Set to IPC_CREAT Do bit by bit or operation with file permissions . Set up IPC_CREAT After the sign , Even if given key It's an existing semaphore key, It doesn't make mistakes . and IPC_CREAT | IPC_EXCL You can create a new , The only semaphore , If the semaphore already exists , Return an error . Generally, we will return the previous file permissions Delete and initialize semaphores :
int semctl(int semid, int semnum, int cmd, ...);
(1)sem_id By semget The returned semaphore identifier
(2)semnum Which semaphore of the current semaphore set
(3)cmd Usually one of the following two values
SETVAL: Used to initialize a semaphore to a known value .p This value is determined by union semun Medium val Member settings , Its function is to set the semaphore before it is used for the first time .
IPC_RMID: Used to delete a semaphore identifier that no longer needs to be used , If you delete it, you don't need the default parameters , Just three parameters .
The fourth parameter is generally set to union semnu arg; The definition is as follows :
union semun
{
int val; // Value used
struct semid_ds *buf; //IPC_STAT、IPC_SET Cache used
unsigned short *arry; //GETALL,、SETALL The array used
struct seminfo *__buf; // IPC_INFO(Linux specific ) Cache used
};Change the value of the semaphore :
int semop(int semid, struct sembuf *sops, size_t nops);
(1)nsops: The number of semaphores for operation , namely sops The number of structural variables , Must be greater than or equal to 1. The most common setting is that this value is equal to 1, Complete the operation of only one semaphore
(2)sembuf Is defined as follows :
struct sembuf{
short sem_num; // Unless you use a set of semaphores , Otherwise it is 0
short sem_op; // Semaphore data that needs to be changed in one operation , It's usually two numbers ,
// One is -1, namely P( wait for ) operation ,
// One is +1, namely V( Sending signal ) operation .
short sem_flg; // Usually it is SEM_UNDO, Make the operating system track semaphores ,
// And when the process terminates without releasing the semaphore , The operating system releases semaphores
}; 边栏推荐
- 2022年危险化学品经营单位安全管理人员考试试题及模拟考试
- 用手机在同花顺上开户靠谱吗?这样炒股有没有什么安全隐患
- 这个国庆!腾讯云WeCity陪您一同出行,点亮城市地标...
- Default methods for Scala sample classes
- 2022 melting welding and thermal cutting recurrent training question bank simulated examination platform operation
- Leetcode 1248. 统计「优美子数组」(害,突然发现只会暴力枚举了)
- Scala IO read by character
- [microservices sentinel] cluster link | microservices cluster environment construction
- Scala trait inheritance class
- 粉丝福利,JVM 手册(包含 PDF)
猜你喜欢

2022r1 quick opening pressure vessel operation test questions and answers
![[microservices sentinel] sentinel quick start | building an image | starting the console](/img/88/a01c8120f6117f1b8e4463cf6f685f.png)
[microservices sentinel] sentinel quick start | building an image | starting the console

2022 crane driver (limited to bridge crane) examination question bank simulated examination platform operation

4 ans d'expérience de travail, 5 modes de communication Multi - thread ne peuvent pas être décrits, vous osez croire?

Introduction to bi-sql wildcards

The basic principle and application of iterator and enhanced for

丹麥技術大學首創將量子計算應用於能源系統潮流建模

Syntax highlighting of rich text

How to store dataframe data in pandas into MySQL

Zuckerberg demonstrated four VR head display prototypes, and meta revealed the "family" of metauniverse
随机推荐
I'd like to ask how to open an account at industrial securities? Is it safe to open a stock account through the link
Convolution and transpose convolution
Mobile security tool jar
Redis + Lua implementation of distributed interface current limiting
The latest QQ wechat domain name anti red PHP program source code + forced jump to open
Sanic服务启动失败
Introduction to smart contract security audit delegatecall (2)
LLVM TargetPassConfig
联想童夫尧:11倍于大势,我们一路攻城拔寨
Bi-sql between
[microservices sentinel] cluster link | microservices cluster environment construction
汇编语言(2)基础知识-debug
扎克伯格上手演示四款VR头显原型机,Meta透露元宇宙「家底」
戴尔为何一直拒绝将商用本的超薄推向极致?
Deep learning LSTM model for stock analysis and prediction
Scala trait construction mechanism
Super detailed description and derivation of convolution and deconvolution (deconvolution is also called transpose convolution and fractional step convolution)
2022 melting welding and thermal cutting recurrent training question bank simulated examination platform operation
Heavyweight: the domestic ide was released, developed by Alibaba, and is completely open source! (high performance + high customization)
最新QQ微信域名防红PHP程序源码+强制跳转打开