Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”
This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money
Today, instead of going over the GO language, we’re going to share some of the C code we wrote earlier, and look at a demo of mutex, spin locking, and atomic operations
The mutex
A critical section resource is occupied by one thread, and when another thread accesses the critical section resource, the CPU switches the thread, and does not allow the subsequent thread to run
It is suitable for locking a lot of content (such as adding nodes with red and black numbers), and the cost of thread switching is less than the cost of waiting
spinlocks
A critical section resource is already occupied by one thread, and when another thread comes to access the critical section resource, it is like a while(1).
Constantly check to see if the resource is available. If it is available, access the critical resource. If not, continue the loop
This applies to things that are less locked (such as ++ operations) and the cost of switching threads is greater than the cost of waiting
Atomic operation
The operations performed are completely indivisible and either all succeed or all fail
The best way to do that is to apply atomic manipulation
In field
Requirement scenarios:
Increment count 100000 times in 10 threads to see if the result is 10*100000
- Create 10 threads in main
- Call inc in thread function to add data
- Use mutexes, spinlocks, and atomic operations to control, respectively
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#define PTHREAD_NUM 10
#define INFO printf
pthread_mutex_t mutex;
pthread_spinlock_t spin;
int inc(int *v,int add)
{
int old;
// assembler, do an atomic operation
__asm__ volatile(
"lock; xaddl %2, %1;"
:"=a" (old)
:"m"(*v),"a"(add)
:"cc"."memory"
);
return old;
}
void * thread_callback(void *arg)
{
int *count = (int *)arg;
int i = 100000;
while(i--)
{
#if 0
/ / the mutex
pthread_mutex_lock(&mutex);
(*count)++;
pthread_mutex_unlock(&mutex);
#elif 0
/ / the spin lock
pthread_spin_lock(&spin);
(*count)++;
pthread_spin_unlock(&spin);
#else
// Atomic operation
inc(count,1);
#endif
usleep(1); }}int main(a)
{
pthread_t thread[PTHREAD_NUM] = {0};
pthread_mutex_init(&mutex,NULL);
pthread_spin_init(&spin,0);
int count = 0;
for(int i = 0; i<PTHREAD_NUM; i++){ pthread_create(&thread[i],NULL,thread_callback,&count);
}
for(int i = 0; i<100; i++) { INFO("count == %d\n",count);
sleep(1);
}
return 0;
}
Copy the code
The code above is simple enough that XDM of interest can run on its own and control itself using mutexes, spinlocks or atomic operations to see how it compares
2. Performance comparison of Mutex, Lock and Atomic
We can use the following code to see how mutex, Lock, and Atomic perform
/ / concurrent
// mutex
// The CPU will be released if the resource is not available
// Usage scenarios
// The shared area executes a lot of content
// spinlock spinlock
// If the resource is not available, it spins in place, busy, etc
// Usage scenarios
// Less content is executed in the shared area
// Atomic operation
// Indivisible
// Usage scenarios
// Do simple ++, -- operations
//
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#define MAX_PTHREAD 2
#define LOOP_LEN 1000000000
#define LOOP_ADD 10000
int count = 0;
pthread_mutex_t mutex;
pthread_spinlock_t spin;
typedef void *(*functhread)(void *arg);
void do_add(int num)
{
int sum = 0;
for(int i = 0; i<num; i++) { sum +=i; }}int atomic_add(int *v,int add)
{
int old;
__asm__ volatile(
"lock; xaddl %2, %1;"
:"=a" (old)
:"m"(*v),"a"(add)
:"cc"."memory"
);
return old;
}
void * atomicthread(void *arg)
{
for(int i = 0; i<LOOP_LEN; i++){ atomic_add(&count,1); }}void * spinthread(void *arg)
{
for(int i = 0; i<LOOP_LEN; i++){ pthread_spin_lock(&spin); count++;//do_add(LOOP_ADD);pthread_spin_unlock(&spin); }}void * mutexthread(void *arg)
{
for(int i = 0; i<LOOP_LEN; i++){ pthread_mutex_lock(&mutex); count++;//do_add(LOOP_ADD);pthread_mutex_unlock(&mutex); }}int test_lock(functhread thre,void * arg)
{
clock_t start = clock();
pthread_t tid[MAX_PTHREAD] = {0};
for(int i = 0; i<MAX_PTHREAD; i++) {// Create a thread
int ret = pthread_create(&tid[i],NULL,thre,NULL);
if(0! = ret) {printf("pthread create rror\n");
return - 1; }}for(int i = 0; i<MAX_PTHREAD; i++){// Reclaim the thread
pthread_join(tid[i],NULL);
}
clock_t end = clock();
//printf("start -- %ld\n",start);
//printf("end -- %ld\n",end);
//printf("CLOCKS_PER_SEC -- %ld\n",CLOCKS_PER_SEC);
printf("spec lock is -- %ld\n",(end - start)/CLOCKS_PER_SEC);
}
int main(a)
{
pthread_mutex_init(&mutex,NULL);
pthread_spin_init(&spin,0);
/ / spin test
count = 0;
printf("use spin ------ \n");
test_lock(spinthread,NULL);
printf("count == %d\n",count);
/ / test mutex
count = 0;
printf("use mutex ------ \n");
test_lock(mutexthread,NULL);
printf("count == %d\n",count);
/ / atomic test
count = 0;
printf("use automic ------ \n");
test_lock(atomicthread,NULL);
printf("count == %d\n",count);
return 0;
}
Copy the code
The results of
From the above results, we can see that the mutex, the spin lock, and the atomic operation all add up correctly as I expected, but there is still some difference in time:
Spin-locks and mutex perform about the same here, but atomic operations are much faster
Welcome to like, follow and favorites
Friends, your support and encouragement, I insist on sharing, improve the quality of the power
All right, that’s it for this time
Technology is open, our mentality, should be more open. Embrace change, live in the sun, and strive to move forward.
I am Nezha, welcome to like, see you next time ~