1. 同步概念
同步:即按时间先后顺序执行。也叫时间控制流。
同步机制:多个控制流访问同一个共享资源时,为了保证数据不混乱而引入的一种协调机制。 2. 线程同步
互斥量:也叫建议锁。因为线程不加锁也可以访问数据但容易出现混乱,建议加锁。#include<pthread.h>
pthread_mutex_t:是结构体变量,可看作值为1或者0
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);//1
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//2
//初始化一把锁的两种方式。看作初始化变量值为1。mutex为传出参数,attr传入属性,restrict意思是限制为只能通过本指针修改内存空间。
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//销毁一把锁。mutex为传入参数。
int pthread_mutex_lock(pthread_mutex_t *mutex);
//加锁,看作mutex--。成功加上锁mutex减1,加不上阻塞等待。
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//尝试加锁mutex--,不阻塞,会立马返回是否加锁成功。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//加锁,看作mutex++。同时将阻塞在该锁的所有线程唤醒。接下来哪个线程获取锁取决于cpu调度。
//加锁解锁应该成对出现,加锁粒度应该尽可能的小。
死锁:对同一互斥量重复加锁、线程1持有互斥锁A请求互斥锁B但线程2持有B请求A。
信号量:互斥量的升级版。看作是初始值为n的互斥量。保证同步的同时提高了并发量。#include<semaphore.h>
sem_t:结构体,n不能小于0.
int sem_init(sem_t *sem, int pshared, unsigned int value);
//初始化。sem传出参数,pshared为0时线程间、非0时进程间。value信号量初始n的值。
int sem_destroy(sem_t *sem);
//销魂信号量,sem传入参数
int sem_wait(sem_t *sem);
//阻塞加锁,加锁成功sem--
int sem_trywait(sem_t *sem);
//尝试加锁,加锁成功sem--
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
//阻塞加锁,有个等待超时时间,加锁成功sem--
int sem_post(sem_t *sem);
//解锁。sem++。唤醒阻塞在该锁的所有线程。接下来哪里线程获得锁不确定取决于cpu调度。
条件变量:不是锁,但可以造成阻塞。与互斥量mutex结合使用。减少不必要的竞争。#include<pthread.h>
pthread_cond_t:结构体
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//初始化一个条件变量的两种方式,cond传出参数,attr传入参数属性
int pthread_cond_destroy(pthread_cond_t *cond);
//销毁一个条件变量,cond传入参数
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
//阻塞等待一个条件变量
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
//阻带等待一个条件变量,有个超时时间。abstime绝对时间,从0开始,所以使用时需要加上当前时间time(NULL)。
int pthread_cond_signal(pthread_cond_t *cond);
//至少唤醒一个阻塞在该条件变量的线程
int pthread_cond_broadcast(pthread_cond_t *cond);
//唤醒所有阻塞在该条件变量的线程
读写锁:读时共享,写时独占。写锁优先级高。适用于读远远大于写的情况。
pthread_rwlock_t:结构体
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
//初始化读写锁的两种方式
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
//销毁读写锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
//请求读锁,rdlock阻塞,tryrdlock不阻塞
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
//请求写锁,wrlock阻塞,trywrlock不阻塞
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
//释放读锁或者写锁。
3. 进程同步
互斥量:使用方法类似与线程同步,初始化时在pthread_mutexattr_t属性添调用pthread_mutexattr_setpshared()设置为PTHREAD_PROCESS_SHARED即可。
信号量:一般与mmap内存共享映射结合使用。
文件锁:fcntl函数实现,只有进程才有文件锁,线程没有因为通过文件修改描述符实现的。
int fcntl(int fd, int cmd, struct flock *fl)
//fd文件描述符,
//cmd为F_SETLK设置文件(trylock)、为F_SETLKW设置文件锁(wait)、为F_GETLK获取文件锁
//struct flock:结构体
l_type:F_RDLCK文件读锁,F_WRLCK文件写锁,F_UNLCK解锁
l_whence:从哪里计算偏移,SEEK_SET开始位置,SEEK_CUR当前位置,SEEK_END结束位置
l_start:起始偏移
l_len:长度。为0表示整个文件加锁
l_pid:持有该锁的进程id。F_GETLK时使用。
4. 生产者消费者模型
#include<stdlib.h>
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
typedef struct msg {
struct msg* next;
int num;
}msg_t;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
msg_t* header;
void* product(void* arg)
{
msg_t* mp;
while (1)
{
pthread_mutex_lock(&mutex);
mp = malloc(sizeof(msg_t));
mp->num = rand() % 1000;
printf("--product ---%d
", mp->num);
mp->next = header;
header = mp;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&has_product);
sleep(rand() % 5);
}
return NULL;
}
void* consume(void* arg)
{
msg_t* mp;
while (1)
{
pthread_mutex_lock(&mutex);
while (header == NULL) {//if null,release the lock and wait to be signalled.
pthread_cond_wait(&has_product, &mutex);
}
if (mp != NULL)
{
free(mp);
mp = NULL;
}
mp = header;
header = mp->next;
printf("--consume ---%d
", mp->num);
free(mp);
mp = NULL;
pthread_mutex_unlock(&mutex);
sleep(rand() % 5);
}
return NULL;
}
int main_pthread_cond_var(int argc, char* argv[])
{
pthread_t pid, cid;
srand(time(NULL));
pthread_create(&pid, NULL, product, NULL);
pthread_create(&cid, NULL, consume, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return EXIT_SUCCESS;
}
5. 哲学家问题
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
#include<sys/mman.h>
#include<sys/wait.h>
#define NUM 5
pthread_mutex_t mutex[NUM];
void* thread_philosopher_run(void* argv)
{
srand(time(NULL));
int i = (int)argv;
int left, right;
if (i == NUM - 1)//the last one do in opposite way
left = 0, right = i;
else
left = i, right = i + 1;
while (1)
{
pthread_mutex_lock(&mutex[left]);
if (pthread_mutex_trylock(&mutex[right]) == 0)
{
printf(" %c is eating
", 'A' + i);
pthread_mutex_unlock(&mutex[right]);
}
pthread_mutex_unlock(&mutex[left]);
sleep(rand() % 5);
}
return NULL;
}
void test_thread_philosopher()
{
pthread_t tid[NUM];
for (int i = 0; i < NUM; i++)
{
pthread_mutex_init(&mutex[NUM], NULL);
}
for (int i = 0; i < NUM; i++)
{
pthread_create(&tid[i], NULL, thread_philosopher_run, (void*)i);
}
for (int i = 0; i < NUM; i++)
{
pthread_join(tid[i], NULL);
}
for (int i = 0; i < NUM; i++)
{
pthread_mutex_destroy(&mutex[i]);
}
}
void test_process_philosopher()
{
int i;
sem_t* sem;
sem = mmap(NULL, sizeof(sem_t) * NUM, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
if (sem == MAP_FAILED)
{
perror("mmap error");
exit(EXIT_FAILURE);
}
for (i = 0; i < NUM; i++)
sem_init(sem + i, 0, 1);
for (i = 0; i < NUM; i++)
if (fork() == 0)
break;
if (i < NUM)
{
int left, right;
srand(time(NULL));
if (i == NUM - 1)
left = 0, right = i;
else
left = i, right = i + 1;
while (1)
{
sem_wait(sem + left);
if (sem_trywait(sem + right) == 0)
{
printf(" %c is eating
", 'A' + i);
sem_post(sem + right);
}
sem_post(sem + left);
sleep(rand() % 5);
}
printf("fork process id=%d
", getpid());
exit(0);
}
for (i = 0; i < NUM; i++)
wait(NULL);
for (int i = 0; i < NUM; i++)
sem_destroy(sem + i);
munmap(sem, sizeof(sem_t) * NUM);
}
int main(int argc, char* argv[])
{
//test_thread_philosopher();
test_process_philosopher();
return EXIT_SUCCESS;
}
注意:编译链接时需要添加参数 -lpthread 线程库。比如gcc -c src.c -o app.exe -lpthread 。