线程同步 - d16¶
一、并发冲突问题¶
g_pool = 0;
线程1 线程1
从内存中把0读取CPU寄存器
把CPU寄存器中的0累加为1
把CPU寄存器中1写回到内存
从内存中把1读取CPU寄存器
把CPU寄存器中的1累减为0
把CPU寄存器中0写回到内存
-------------------------------------------------------------
从内存中把0读取CPU寄存器
从内存中把0读取CPU寄存器
把CPU寄存器中的0累加为1
把CPU寄存器中的0累减为-1
把CPU寄存器中-1写回到内存
把CPU寄存器中1写回到内存
当多个线程同时访问共享资源时,
由于对共享资源的非原子化操作,
可以引发数据不一致的问题。
这种现象被称为并发冲突。
二、互斥量¶
多线程 X
并发访问共享资源 X
非原子化操作 V
将一组非原子化的指令变成原子化
――任何时候都只会有一个线程执行一组特定的指令。
线程1 {
锁定互斥量;
...... \
...... > 原子化操作
...... /
解锁互斥量;
}
线程2 {
锁定互斥量;
...... \
...... > 原子化操作
...... /
解锁互斥量;
}
// 初始化互斥量:
int pthread_mutex_init (pthread_mutex_t* mutex,
const pthread_mutexattr_t* mutexattr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 锁定互斥量:
int pthread_mutex_lock (pthread_mutex_t* mutex);
// 解锁互斥量:
int pthread_mutex_unlock (pthread_mutex_t* mutex);
// 销毁互斥量:
int pthread_mutex_destroy (pthread_mutex_t* mutex);
// 针对通过pthread_mutex_init初始化的互斥量。
三、资源分享问题¶
当多个线程竞争有限资源时,会出现某些线程得不到资源的情况。 这时得不到资源的线程应该等待那些获得资源的线程,在使用资源以后,主动放弃资源。
四、信号量¶
信号量是一个资源计数器,用于控制访问有限资源的线程数。
#include <semaphore.h>
/* 1.创建信号量 */
int sem_init (
sem_t* sem, // 输出,信号量ID
int pshared, // 0表示线程信号量,非零表示进程信号量
unsigned int value // 信号量初值
);
// 成功返回0,失败返回-1。
/* 2.等待信号量 */
int sem_wait (sem_t* sem);
// 如果信号量sem的计数值足够减1,立即返回0,
// 同时将信号量sem的计数值减1,不够减则阻塞,直到够减为止。
int sem_trywait (sem_t* sem);
// 如果信号量sem的计数值不够减,不阻塞而是立即返回-1,
// 同时将errno置为EAGAIN。
int sem_timedwait (sem_t* sem,
const struct timespec* abs_timeout /* 等待超时 */);
struct timespec {
time_t tv_sec; // 秒
long tv_nsec; // 纳秒(10^-9秒)
};
// 如果信号量sem的计数值不够减即阻塞,直到够减或超时返回,
// 如果因为超时返回,errno为ETIMEOUT。
/* 3.释放信号量 */
int sem_post (sem_t* sem);
// 将信号量sem的计数值加1。
/* 4.销毁信号量 */
int sem_destroy (sem_t* sem);
// 释放信号量对象本身的内核资源。
五、死锁问题¶
线程1 线程2
| |
获取A 获取B
| |
获取B 获取A <- 死锁
| \ / |
释放B X 释放A
| / \ |
释放A 释放B
/*
* bus.c
*/
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
int g_cn = 5; // 空座位
pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
sem_t g_sem;
// 乘客线程
void* thread_proc (void* arg) {
sem_wait (&g_sem);
pthread_mutex_lock (&g_mutex);
printf ("%lu坐下,还剩%d个空座。\n", pthread_self (), --g_cn);
pthread_mutex_unlock (&g_mutex);
usleep (50000); // 坐车时间
pthread_mutex_lock (&g_mutex);
printf ("%lu下车,还剩%d个空座。\n", pthread_self (), ++g_cn);
pthread_mutex_unlock (&g_mutex);
sem_post (&g_sem);
return NULL;
}
int main (void) {
sem_init (&g_sem, 0, g_cn);
printf ("发车时共有%d个空座。\n", g_cn);
pthread_t tids[20];
int i;
for (i = 0; i < 20; ++i){
pthread_create (&tids[i], NULL, thread_proc, NULL);
}
for (i = 0; i < 20; ++i){
pthread_join (tids[i], NULL);
}
printf ("收车时共有%d个空座。\n", g_cn);
sem_destroy (&g_sem);
return 0;
}
/*
* dead.c
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t g_a = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t g_b = PTHREAD_MUTEX_INITIALIZER;
void* thread1 (void* arg) {
pthread_t tid = pthread_self ();
printf ("%lu线程:等待A...\n", tid);
pthread_mutex_lock (&g_a);
printf ("%lu线程:获得A!\n", tid);
sleep (1);
printf ("%lu线程:等待B...\n", tid);
pthread_mutex_lock (&g_b);
printf ("%lu线程:获得B!\n", tid);
// ...
pthread_mutex_unlock (&g_b);
printf ("%lu线程:释放B。\n", tid);
pthread_mutex_unlock (&g_a);
printf ("%lu线程:释放A。\n", tid);
return NULL;
}
void* thread2 (void* arg) {
pthread_t tid = pthread_self ();
printf ("%lu线程:等待B...\n", tid);
pthread_mutex_lock (&g_b);
printf ("%lu线程:获得B!\n", tid);
sleep (1);
printf ("%lu线程:等待A...\n", tid);
pthread_mutex_lock (&g_a);
printf ("%lu线程:获得A!\n", tid);
// ...
pthread_mutex_unlock (&g_a);
printf ("%lu线程:释放A。\n", tid);
pthread_mutex_unlock (&g_b);
printf ("%lu线程:释放B。\n", tid);
return NULL;
}
int main (void) {
pthread_t tid1;
pthread_create (&tid1, NULL, thread1, NULL);
pthread_t tid2;
pthread_create (&tid2, NULL, thread2, NULL);
pthread_join (tid1, NULL);
pthread_join (tid2, NULL);
return 0;
}
#include <stdio.h>
#include <string.h>
#include <pthread.h>
int g_pool = 0;
pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
void* pool1 (void* arg) {
unsigned int i;
pthread_mutex_lock (&g_mutex);
for (i = 0; i < 100000000; ++i) {
// pthread_mutex_lock (&g_mutex);
++g_pool;
// pthread_mutex_unlock (&g_mutex);
}
pthread_mutex_unlock (&g_mutex);
return NULL;
}
void* pool2 (void* arg) {
unsigned int i;
pthread_mutex_lock (&g_mutex);
for (i = 0; i < 100000000; ++i) {
// pthread_mutex_lock (&g_mutex);
--g_pool;
// pthread_mutex_unlock (&g_mutex);
}
pthread_mutex_unlock (&g_mutex);
return NULL;
}
int main (void) {
//pthread_mutex_init (&g_mutex, NULL);
pthread_t tid1;
pthread_create (&tid1, NULL, pool1, NULL);
pthread_t tid2;
pthread_create (&tid2, NULL, pool2, NULL);
pthread_join (tid1, NULL);
pthread_join (tid2, NULL);
printf ("%u\n", g_pool);
//pthread_mutex_destroy (&g_mutex);
return 0;
}
all:
gcc -m32 -o bus bus.c -lpthread
gcc -m32 -o dead dead.c -lpthread
gcc -m32 -o pool pool.c -lpthread
clean:
rm bus dead pool