同步与互斥:
互斥是指两个线程不能同时运行,他们会相互排斥,必须等一个线程运行完,另一个才能运行;
同步也是不能同时运行,但是他们必须按照一定的顺序执行。
一、线程
线程的数据类型为:pthread_t
#include <pthread.h>
pthread_t pthread_self(void); //获取线程id
int pthread_create( pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg ); //线程的创建
/*pthread:线程id的地址
* attr:线程参数,一般为NULL
* start_routine:线程的入口函数
* arg:函数的形参
* 成功:0, 失败:非0
*/
int pthread_join(pthread_t thread, void **retval); //回收线程资源,阻塞,成功:0,失败:非0
int pthread_detach(pthread_t thread); //回收线程资源,非阻塞
/*使线程和当前进程分离,分离后不代表线程不依赖于当前进程,线程分离的目的是将线程的回收工作交给系统*/
void pthread_exit(void *retval); //线程退出
二、互斥锁
线程里有一把锁–互斥锁,互斥锁是一种简单的加锁方式控制对共享资源的访问,互斥锁只有两种状态:加锁和解锁。
互斥锁的操作流程:
(1) 在访问共享资源临界区前,对互斥锁进行加锁
(2) 在访问完释放互斥锁
(3) 在堆互斥锁进行加锁之后,任何试图再次对互斥锁进行加锁的线程都将会被阻塞,直到锁被释放
互斥锁的数据类型:pthread_mutex_t
1、原型
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
/*初始化一个互斥锁,mutex:互斥锁地址,attr:互斥锁属性,一般为NULL
*成功:0,成功申请的锁默认是打开的, 失败:-1
*/
int pthread_mutex_lock(pthread_mutex_t *mutex); //上锁,阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex); //上锁,非阻塞,若已经上锁,返回错误,即EBUSY
int pthread_mutex_unlock(pthread_mutex_t *mutex); //解锁
int pthread_mutex_destroy(pthread_mutex_t *mutex); //销毁
/*利用互斥锁实现两个线程对打印程序的互斥访问*/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mutex; //互斥锁
//打印程序
void printer(char *str)
{
pthread_mutex_lock(&mutex);
while (1)
{
putchar(*str);
fflush(stdout);
str++;
sleep(1);
}
printf("\n");
pthread_mutex_unlock(&mutex);
}
//线程1
void *pthread_func1(void *arg)
{
char *str = "hello";
printer(str);
}
//线程2
void *pthread_func2(void *arg)
{
char *str = "world";
printer(str);
}
int main()
{
pthread_t tid1, tid2;
pthread_mutex_init(&mutex, NULL); //初始化互斥锁
pthread_create(&tid1, NULL, pthread_func1, NULL);
pthread_create(&tid2, NULL, pthread_func2, NULL);
pthread_mutex_destory(&mutex); //销毁互斥锁
return 0;
}
三、读写锁
当一个线程已经持有互斥锁,互斥锁会将所有试图进入临界区的其他线程阻塞住。但是考虑这样一种场景:如果持有互斥锁的线程只是想读访问共享资源,其他的线程也只是想读访问共享资源,但是由于互斥锁的排他性,其他线程是没法获取到锁去访问共享资源的。
为了解决上面的问题,线程提供了另一种机制:读写锁。
读写锁的规则如下:
1) 如果某线程申请了读锁,其他线程可以再申请读锁,但不能申请写锁;
2) 如果某线程申请了写锁,其他线程不能申请读锁,也不能申请写锁。
POSIX定义的读写锁的数据类型为:pthread_rwlock_t
1、原型
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr *attr);
/*初始化rwlock所指向的读写锁
*成功:0,读写锁的初始状态是解锁的 失败:-1
*/
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock ); //申请读锁,阻塞
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); //申请读锁,不阻塞
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock ); //申请写锁,阻塞
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); //申请写锁,不阻塞
int pthread_rwlock_unlock (pthread_rwlock_t *rwlock); //解锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); //销毁
/*读写锁实现4个线程的互斥读写,两个线程申请读锁读数据,两个线程申请写锁写数据*/
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
int num = 1; //全局共享数据
pthread_rwlock_t rwlock;
//pthread 1
void pthread_func1(void *arg)
{
while (1)
{
pthread_rwlock_rdlock(&rwlock);
printf("process 1 read num:%d.\n", num);
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
}
//pthread 2
void pthread_func2(void *arg)
{
while (1)
{
pthread_rwlock_rdlock(&rwlock);
printf("process 2 read num:%d.\n", num);
pthread_rwlock_unlock(&rwlock);
sleep(2);
}
}
//process 3
void pthread_func3(void *arg)
{
while (1)
{
pthread_rwlock_wrlock(&rwlock);
num++;
printf("After process 3 write num:%d.\n", num);
pthread_rwlock_unlock(&rwlock);
sleep(2);
}
}
//process 4
void pthread_func4(void *arg)
{
while (1)
{
pthread_rwlock_wrlock(&rwlock);
num++;
printf("After process 4 write num:%d.\n", num);
pthread_rwlock_unlock(&rwlock);
sleep(2);
}
}
int main()
{
pthread_t tid1, tid2, tid3, tid4;
pthread_rwlock_init(&rwlock, NULL); //初始化读写锁
//创建线程
pthread_create(&tid1, NULL, pthread_func1, NULL);
pthread_create(&tid2, NULL, pthread_func2, NULL);
pthread_create(&tid3, NULL, pthread_func3, NULL);
pthread_create(&tid4, NULL, pthread_func4, NULL);
//等待线程结束
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
pthread_join(tid4, NULL);
return 0;
}