如果多个线程访问相同的数据,并且它们中至少有一个修改了数据,那么对共享数据的所有访问必须同步以防止数据竞争。但是,一个正在读取共享数据的线程可能中断另一个正在修改相同共享数据的线程,因此,可能导致线程读取到不一致的数据。
锁的类型分几种,分别是互斥锁,自旋锁,读写锁,以及原子操作,锁的粒度依次降低
互斥锁
mutex是睡眠等待(sleep waiting)类型的锁,当线程抢互斥锁失败的时候,线程会陷入休眠。优点就是节省CPU资源,缺点就是休眠唤醒会消耗一点时间。另外自从Linux 2.6版以后,mutex完全用futex的API实现了,内部系统调用的开销大大减小。
//互斥锁定义
pthread_mutex_t mutex;
//互斥锁初始化
pthread_mutex_init(&mutex, NULL);
//互斥锁加锁与解锁
pthread_mutex_lock(&mutex);
(*pcount) ++;
pthread_mutex_unlock(&mutex);if (0 != pthread_mutex_trylock(&mutex)) {
i --;
continue;
}
(*pcount) ++;
pthread_mutex_unlock(&mutex);pthread_mutex_trylock用于以非阻塞的模式来请求互斥量。就好比各种IO函数都有一个noblock的模式一样,对于加锁这件事也有类似的非阻塞模式。当线程尝试加锁时,如果锁已经被其他线程锁定,该线程就会阻塞住,直到能成功acquire。但有时候我们不希望这样。pthread_mutex_trylock在被其他线程锁定时,会返回特殊错误码。加锁成返回0,仅当成功但时候,我们才能解锁在后面进行解锁操作!
自旋锁是一种非阻塞锁,也就是说,如果某线程需要获取自旋锁,但该锁已经被其他线程占用时,该线程不会被挂起,而是在不断的消耗CPU的时间,不停的试图获取自旋锁。
//自旋锁定义
pthread_spinlock_t spinlock;
//自旋锁初始化
pthread_spin_init(&spinlock, PTHREAD_PROCESS_SHARED);
//自旋锁枷锁与解锁
pthread_spin_lock(&spinlock);
(*pcount) ++;
pthread_spin_unlock(&spinlock);
读写锁是用来解决读者写者问题的,读操作可以共享,写操作是排他的,读可以有多个在读,写只有唯一个在写,同时写的时候不允许读。
//读写锁定义
pthread_rwlock_t rwlock;
//读写锁初始化
pthread_rwlock_init(&rwlock, NULL);
//写加锁
pthread_rwlock_wrlock(&rwlock);
(*pcount) ++;
pthread_rwlock_unlock(&rwlock);
//读加锁
pthread_rwlock_rdlock(&rwlock);
printf("count --> %d\n", count);
pthread_rwlock_unlock(&rwlock);
原子操作
原子操作
(atomic operation)
不需要synchronized
。原子操作指的是不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何context switch
(切换到另一个线程)int inc(int *value, int add) { int old; __asm__ volatile ( "lock; xaddl %2, %1;" // "lock; xchg %2, %1, %3;" : "=a" (old) : "m" (*value), "a" (add) : "cc", "memory" ); return old; }
多进程同步中,竞争资源需要可以存于共享内存之中
共享内存申请:
nt *pcount = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
//完整版锁测试代码如下
#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
// MOV src, dest; x86
int inc(int *value, int add) {
int old;
__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
if (0 != pthread_mutex_trylock(&mutex)) {
i --;
continue;
}
(*pcount) ++;
pthread_mutex_unlock(&mutex);
#elif 0
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 0
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
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) {
usleep(1);
break;
}
}
if (pid > 0) { //
for (i = 0;i < 100;i ++) {
printf("count --> %d\n", (*pcount));
sleep(1);
}
} else {
int i = 0;
while (i++ < 100000) {
#if 0
(*pcount) ++;
#else
inc(pcount, 1);
#endif
usleep(1);
}
}
#endif
}