锁的由来
在多线程或多进程下,有些变量需要共享使用就会产生资源竞争,这个资源称为临界资源
互斥锁
自旋锁
原子操作
本质多条指令,合成一个执行
理想情况下多个线程执行 idx++,每个线程都能完整执行完Idx++,再切换
可能出现在线程和CPU切换时,线程1的idx已经拷贝到寄存器中,还未同步++,再从寄存器读出来就发生线程切换,这样线程2取的值依然是原本的
最后导致,两个线程只++了一次
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#define THREAD_SIZE 10
int count = 0;
pthread_mutex_t mutex;//互斥锁
pthread_spinlock_t spinlock;//自旋锁
pthread_rwlock_t rwlock;//读写锁
// MOV dest, src; at&t Linux
// MOV src, dest; x86 Windows 汇编器实现语法不一样
// 原子操作
int inc(int *value, int add) {
int old;
//不定义的时候 优先寄存器的值 编译器会优化 加载到寄存器 改变再写回内存
//volatile 易变的 直接操作内存的值 CPU计算时候会直接取它
//内存屏障 汇编方式
__asm__ volatile (
"lock; xaddl %2, %1;" // "lock; xchg %2, %1, %3;"
: "=a" (old)
: "m" (*value), "a" (add)
: "cc", "memory"
);
return old;
}
//
void *func(void *arg) {
int *pcount = (int *)arg;
int i = 0;
while (i++ < 100000) {
#if 0
(*pcount) ++;//不加锁 自增
#elif 0
//使用互斥锁
pthread_mutex_lock(&mutex);
(*pcount) ++;
pthread_mutex_unlock(&mutex);
#elif 0
//尝试加锁 不休眠 但是运行消耗大,且可能线程切换 反应更快 成功返回0
if (0 != pthread_mutex_trylock(&mutex)) {
i --;
continue;
}
(*pcount) ++;
pthread_mutex_unlock(&mutex);
#elif 1
//自旋锁 不会发生线程切换 且不会休眠 自动循环尝试加锁
pthread_spin_lock(&spinlock);
(*pcount) ++;
pthread_spin_unlock(&spinlock);
#elif 0
//读写锁 多线程 只允许一个线程写 允许多个线程读
pthread_rwlock_wrlock(&rwlock);
(*pcount) ++;
pthread_rwlock_unlock(&rwlock);
#else
inc(pcount, 1);
#endif
usleep(1);
}
}
int main() {
#if 1
//10个线程 对一个count变量 ++ ,每个线程执行10w次 理论 count最终等于100W 但实际不加锁情况是<100W
pthread_t threadid[THREAD_SIZE] = {0};
pthread_mutex_init(&mutex, NULL);//互斥锁初始化
pthread_spin_init(&spinlock, PTHREAD_PROCESS_SHARED);//自旋锁初始化
pthread_rwlock_init(&rwlock, NULL);//读写锁初始化
int i = 0;
int count = 0;
for (i = 0;i < THREAD_SIZE;i ++) {
int ret = pthread_create(&threadid[i], NULL, func, &count);
if (ret) {
break;
}
}
for (i = 0;i < 100;i ++) {
//pthread_rwlock_rdlock(&rwlock);
printf("count --> %d\n", count);
//pthread_rwlock_unlock(&rwlock);
sleep(1);
}
#else
//多进程下 加锁
//共享内存
//1.fd NULL 2.分配int大小 3.可读可写 4.以xx方式映射 5.-1 6.0
// 这个适合父子进程 不适合文件打开
int *pcount = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
int i = 0;
pid_t pid = 0;
for (i = 0;i < THREAD_SIZE;i ++) {
//循环创建多个进程
pid = fork();
if (pid <= 0) {
//这里 pid=0 是子进程,就不要fork了
usleep(1);//fork完后 让子进程先行
break;
}
}
//父进程就是主进程负责打印
if (pid > 0) {
//执行打印
for (i = 0;i < 100;i ++) {
printf("count --> %d\n", (*pcount));
sleep(1);
}
} else {
//其他10个进程负责
int i = 0;
while (i++ < 100000) {
#if 1
(*pcount) ++;
#else
inc(pcount, 1);
#endif
usleep(1);
}
}
#endif
}