当前位置:网站首页>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
}; 边栏推荐
- 戴尔为何一直拒绝将商用本的超薄推向极致?
- [microservices sentinel] cluster link | microservices cluster environment construction
- Transform BeanUtils to achieve list data copy gracefully
- Using macro code to generate handwriting automatically in word or WPS
- Mobile security tool jar
- 我想问一下兴业证券怎么开户?通过链接办理股票开户安全吗
- Heavyweight: the domestic ide was released, developed by Alibaba, and is completely open source! (high performance + high customization)
- Deploy a production cluster using Loki microservice pattern
- The optimism about technology is making Dell achieve more than expected
- Deep learning LSTM model for stock analysis and prediction
猜你喜欢

汇编语言(2)基础知识-debug

重磅:国产IDE发布,由阿里研发,完全开源!(高性能+高定制性)

Première application de l'informatique quantique à la modélisation des flux de puissance dans les systèmes énergétiques à l'Université technique danoise
![[microservices sentinel] cluster link | microservices cluster environment construction](/img/75/3cc6abf1e72d651b082415db9db46c.png)
[microservices sentinel] cluster link | microservices cluster environment construction

Danish Technical University pioneered the application of quantum computing to power flow modeling of energy system
最新QQ微信域名防红PHP程序源码+强制跳转打开
![[redis realizes seckill business ③] specific implementation of optimistic lock for oversold problem](/img/01/5ec4e5dae1748dce3d5dee33a24b0b.png)
[redis realizes seckill business ③] specific implementation of optimistic lock for oversold problem

Add information on the left and add parts on the right of the status bar

Mobile security tool jar

Scala IO read by character
随机推荐
最新QQ微信域名防红PHP程序源码+强制跳转打开
4 years of working experience, and you can't tell the five communication modes between multithreads. Can you believe it?
How much commission does CICC wealth securities open an account? Is stock account opening and trading safe and reliable?
Bi-sql between
腾讯云WeCity解决方案
Which securities company should I choose to open an account online? Is it safe to open an account online?
Distinguish between i++ and ++i seconds
Danish Technical University pioneered the application of quantum computing to power flow modeling of energy system
2022年全国最新消防设施操作员(高级消防设施操作员)模拟题及答案
Text editor for QT project practice -- Episode 9
LLVM TargetPassConfig
The interview questions and answers for the high-frequency software test of Dachang help you prepare for the golden nine silver ten
腾讯云WeCity丨你好 2022!
ImageView shows network pictures
Golang示例续期锁:Redis+Channel+sync.Mutex
智能合约安全审计入门篇 —— delegatecall (2)
Bi-sql delete
ImageView展示网络图片
activity生命周期
Mobile security tool jarsigner