1.线程的概念
1.1什么是线程
- 线程:是一个进程并发执行多种任务的机制。
- 并发:单核cpu多个任务同时运行。(cpu以ms级别的速度进行进程调度,切换进程、线程)
串行、并发、并行
串行:多个任务有序执行,一个任务执行完毕后,再去执行另外一个任务
并发:多个任务在单核cpu上运行,同一个时间片上只能运行一个任务,cpu不停在各个任务上切换。
并行:多任务在多核cpu上运行,多个任务可以同时在不同的cpu核上执行。
-
进程的上下文切换
-
- 上下文:运行一个进程所需要的所有资源。
- 上下文切换:切换进程的时候,cpu访问的资源从访问A替换原有内容,到访问B。这个过程是一个耗时操作
- 为了提高系统性能,引入了一个轻量级进程的概念,称之为线程。
-
线程运行在进程的空间内,线程是任务运行的最小单位!
-
每一个进程至少需要一个线程作为指令执行体。
-
每个进程中可以运行多个线程,称之为多线程。线程与线程执行根据cpu时间片进行调度
1.2 线程是任务运行的最小单位(重点!!)
属于同一个进程下的线程,共享其附属进程的所有资源。
共享:
- 静态存储区: .bss .data .rodata (全局变量, 静态变量)
- 堆区
- 栈区:
不共享:
- 线程tid号,线程调度块等等…
1.3 进程和线程的区别(重点!!!!)
- 进程是资源分配的最小单位,线程是任务运行的最小单位;
- 进程与进程之间的用户空间相互独立,内核空间共享。进程之间如果要做数据通信,需要引入进程间通信机制(IPC).
- 线程与线程之间共享其附属进程的所有资源。所以线程之间通信不需要通信机制,但是要注意同步互斥。
- 多线程的效率比多进程高。
- 多进程的稳定性比多线程高。
- 多进程的资源量比多线程多。
2.线程的函数
Compile and link with -pthread. 编译的时候需要手动连接线程库
如(vim编译时): gcc main.c -pthread
2.1 pthread_create(创建线程)
功能: 创建一个线程
原型:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
参数:
-
pthread_t *thread:该指针指向的内存空间存储创建成功后的线程tid号;
-
pthread_attr_t attr:线程属性,一般填NULL,代表默认属性。
如果要修改属性,需要用pthread_attr_init函数初始化线程属性,可以设置为分离属性; -
void *(*tart_routine) (void *):回调函数,指定创建成功后的线程所要执行的任务;
函数指针,可以指向返回值是void类型,参数列表是void类型的函数。ps:函数名就是函数首地址
void* function(void* arg){ } -
void *arg:传递给回调函数的参数;
返回值:
- 成功,返回0;
- 失败,返回error_number,没有说更新errno,所以无法用perror打印错误信息;
注意:
- 主线程退出后会导致进程退出,线程运行在进程空间内。进程结束,线程也会强制结束;
- 分支线程退出,不会影响主线程执行。且分支线程退出后不会运行到主线程的代码。
- 主线程与分支线程共享其附属进程的所有资源。
2.1.1主线程局部变量传参给分支线程
2.1.2将分支线程的局部变量传参给主线程
1.方式一
2.方式二
2.2 pthread_exit(退出线程)
功能: 退出线程
原型:
#include <pthread.h>
void pthread_exit(void *retval);
参数:
- void *retval:传递线程退出状态值,该状态值可以被 pthread_join 函数接收。如果不想传递则填NULL即可;
2.3 pthread_join(等待线程退出)
功能:阻塞函数,阻塞等待指定的线程退出,接收退出线程返回的值,并回收退出线程的资源。
原型:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
**参数: **
-
pthread_t thread:指定要回收哪个线程,填对应的tid号;
-
void **retval:如果不为NULL,则会将 pthread_exit 传递的退出状态复制到该二级指针指向的一级指针中。为NULL,不接收线程退出状态值;
返回值:
- 成功,返回0;
- 失败,返回 error_number ,没有说更新errno,所以无法用perror打印错误信息;
2.4 pthread_detach(分离线程)
功能:分离线程,线程退出后资源由内核自动回收
原型:
#include <pthread.h>
int pthread_detach(pthread_t thread);
**参数:**pthread_t thread:指定要分离的线程;
返回值:
- 成功,返回0;
- 失败,返回error_number,没有说更新 errno ,所以无法用 perror 打印错误信息;
pthread_t tid;
if(pthread_create(&tid, NULL, callBack, NULL) != 0)
{
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
return -1;
}
pthread_detach(tid); //分离线程,分离后不需要其他线程调用join接收
注意:
当调用pthread_detach函数分离了分支线程A,此时其他线程调用pthread_join函数回收A线程将无效。pthread_join函数回收A,不会阻塞!!!
2.5 pthread_cancel(请求线程退出)
功能:请求指定线程退出; 请求成功不代表线程会 退出
原型:
#include <pthread.h>
int pthread_cancel(pthread_t thread);
参数:
- pthread_t thread:指定要请求的线程tid号:
返回值:
- 成功,返回0;
- 失败,返回error_number,没有说更新errno,所以无法用perror打印错误信息;
注:
1. pthread_cancel会给目标线程打上一个退出标识,cpu切换到目标线程后,运行到退出标识,才会让目标线程退出。
2. 但是while for 等循环结构,以及算数运算等等位置,无法打上退出标识。所以当目标线程只有上述结构的时候,无法用pthread_cancel退出线程
3. printf ,sleep等等函数结构可以打上退出标识
4. 请求成功,不代表目标线程一定会退出
3. 线程同步互斥机制
临界资源(共享资源):多个任务并发执行的时候,访问同一个资源,我们将这个资源称之为临界资源。
临界区:访问临界资源的代码,称之为临界区
线程之间,如果要进行通信,需要引入同步互斥机制,避免产生竞态。保证任何一个时刻,只有一个线程处理临界资源。
同步互斥机制:
- 互斥锁 (pthread_mutex_t)
- 条件变量 (pthread_cond_t)
- 信号量 (sem_t)
3.1 互斥锁
3.1.1工作原理
-
对于要访问临界资源的线程,在访问之前先申请互斥锁。
-
- 如果申请上锁成功,则进入临界区执行临界区代码,直到退出临界区,解开互斥锁。
- 如果申请上锁失败,则说明互斥锁被别的线程占用,线程进入休眠,等待互斥锁解开。
-
互斥锁只能保证临界区完整,只有一个线程访问,但是无法指定访问者的顺序。
3.1.2 pthread_mutex_init(创建并初始化互斥锁)
功能:创建并初始化互斥锁
原型:
#include <pthread.h>
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;//用于创建初始化的宏
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); //创建初始化的函数
参数:
- pthread_mutex_t *mutex:该指针指向的内存空间,存储申请后的互斥锁
- const pthread_mutexattr_t *mutexattr:互斥锁属性,该属性可以指定互斥锁是用于进程还是用于线程的同步互斥。 填NULL代表用于线程.。
返回值: 永远返回0(即,永远成功)
3.1.3 pthread_mutex_lock(上锁)
功能:对互斥锁进行上锁,若有其他线程占用互斥锁,该函数会阻塞
原型:
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
参数:
- pthread_mutex_t *mutex; 要上锁的指针
返回值:
-
成功,返回0;
-
失败,返回非0,没有说更新errno,所以不要用perror打印错误。
3.1.4 pthread_mutex_unlock(解锁)
功能:解开互斥锁
原型:
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数:
- pthread_mutex_t *mutex; //待解锁的指针
返回值:
- 成功,返回0;
- 失败,返回非0,没有说更新errno,所以不要用perror打印错误。
3.1.5 pthread_mutex_destroy(解锁)
功能:销毁互斥锁
原型:
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex)
参数:
- pthread_mutex_t *mutex;
返回值:
-
成功,返回0;
-
失败,返回非0,没有说更新errno,所以不要用perror打印错误。
3.1.6 死锁
拥有锁资源的任务没有释放锁
- 持有互斥锁的线程异常退出,没有释放锁资源。
- 同一线程对一把互斥锁重复上锁。
- 互斥锁交叉嵌套。
注:写代码的时候要注意,互斥锁是否解开
3.1.7 互斥锁的示例代码
#include <stdio.h>
#include <pthread.h>
#include <string.h>
//临界资源
char buf[] = "1234567";
//互斥锁,采用宏的方式定义"锁"
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* callBack_print(void* arg)//循环打印函数
{
while(1)
{
/***********临界区***********/
pthread_mutex_lock(&mutex); //上锁
printf("%s\n", buf);
pthread_mutex_unlock(&mutex); //解锁
/***********临界区***********/
}
pthread_exit(NULL);
}
void* callBack_swap(void* arg)//循环逆置函数
{/**** 采用首尾地址的方式****/
char* start, *end;
char temp;
while(1)
{
/***********临界区***********/
pthread_mutex_lock(&mutex); //上锁
start = buf;
end = buf+strlen(buf)-1;
while(start < end)
{
temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
pthread_mutex_unlock(&mutex); //解锁
/***********临界区***********/
}
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
//创建互斥锁 方式一
//pthread_mutex_init(&mutex, NULL);
pthread_t tid_p, tid_s;
if(pthread_create(&tid_p, NULL, callBack_print, (void*)&mutex) != 0)
{
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
return -1;
}
pthread_detach(tid_p); //分离线程
if(pthread_create(&tid_s, NULL, callBack_swap, NULL) != 0)
{
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
return -1;
}
pthread_join(tid_s, NULL); //阻塞等待tid_s线程退出
pthread_mutex_destroy(&mutex); //销毁互斥锁
return 0;
}
练习:要求用两个线程拷贝一张图片。
A线程拷贝前半部分,B线程拷贝后半部分,不允许使用sleep函数
方式一:开一次资源,用互斥锁记录偏移量
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
//需要传入到分支线程的资源
struct filemsg
{
int fd_r;
int fd_w;
off_t size;
};
//互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* callBack1(void* arg) //void* arg = &info
{
int fd_r = ((struct filemsg*)arg)->fd_r;
int fd_w = ((struct filemsg*)arg)->fd_w;
off_t size = ((struct filemsg*)arg)->size;
//将偏移量修改到文件开头
off_t offset = 0;
//循环拷贝前半部分
char c = 0;
for(int i=0; i<size/2; i++)
{
/***********临界区***************/
pthread_mutex_lock(&mutex); //上锁
lseek(fd_r, offset, SEEK_SET);
lseek(fd_w, offset, SEEK_SET);
if(read(fd_r, &c, 1) <=0 )
{
perror("read");
return NULL;
}
if(write(fd_w, &c , 1) < 0)
{
perror("write");
return NULL;
}
offset = lseek(fd_r, 0, SEEK_CUR);
pthread_mutex_unlock(&mutex); //解锁
/***********临界区***************/
}
printf("前半部分拷贝完毕\n");
pthread_exit(NULL);
}
void* callBack2(void* arg)
{
int fd_r = ((struct filemsg*)arg)->fd_r;
int fd_w = ((struct filemsg*)arg)->fd_w;
off_t size = ((struct filemsg*)arg)->size;
//将偏移量修改到文件size/2
off_t offset = size/2;
//循环拷贝前半部分
char c = 0;
for(int i=size/2; i<size; i++)
{
/***********临界区***************/
pthread_mutex_lock(&mutex); //上锁
lseek(fd_r, offset, SEEK_SET);
lseek(fd_w, offset, SEEK_SET);
if(read(fd_r, &c, 1) <=0 )
{
perror("read");
return NULL;
}
if(write(fd_w, &c , 1) < 0)
{
perror("write");
return NULL;
}
offset = lseek(fd_r, 0, SEEK_CUR);
pthread_mutex_unlock(&mutex); //解锁
/***********临界区***************/
}
printf("后半部分拷贝完毕\n");
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
int fd_r = open("./1.png", O_RDONLY);
if(fd_r < 0)
{
perror("open");
pthread_exit(NULL);
}
int fd_w = open("./2.png", O_WRONLY|O_CREAT|O_TRUNC, 0664);
if(fd_w < 0)
{
perror("open");
return -1;
}
//计算文件大小
off_t size = lseek(fd_r, 0, SEEK_END);
struct filemsg info;
info.fd_r = fd_r;
info.fd_w = fd_w;
info.size = size;
pthread_t tid1, tid2;
if(pthread_create(&tid1, NULL, callBack1, &info) != 0)
{
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
return -1;
}
if(pthread_create(&tid2, NULL, callBack2, &info) != 0)
{
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
return -1;
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
close(fd_r);
close(fd_w);
//销毁互斥锁
pthread_mutex_destroy(&mutex);
return 0;
}
方式二:开两份资源,线程各自运行各自的
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
void* callBack1(void* arg)
{
int fd_r = open("./1.png", O_RDONLY);
if(fd_r < 0)
{
perror("open");
pthread_exit(NULL);
}
int fd_w = open("./2.png", O_WRONLY);
if(fd_w < 0)
{
perror("open");
close(fd_r);
pthread_exit(NULL);
}
//计算文件大小
off_t size = lseek(fd_r, 0, SEEK_END);
//将偏移量修改到文件开头
lseek(fd_r, 0, SEEK_SET);
lseek(fd_w, 0, SEEK_SET);
//循环拷贝前半部分
char c = 0;
for(int i=0; i<size/2; i++)
{
read(fd_r, &c, 1);
write(fd_w, &c , 1);
}
printf("前半部分拷贝完毕\n");
close(fd_r);
close(fd_w);
pthread_exit(NULL);
}
void* callBack2(void* arg)
{
int fd_r = open("./1.png", O_RDONLY);
if(fd_r < 0)
{
perror("open");
pthread_exit(NULL);
}
int fd_w = open("./2.png", O_WRONLY);
if(fd_w < 0)
{
perror("open");
close(fd_r);
pthread_exit(NULL);
}
//计算文件大小
off_t size = lseek(fd_r, 0, SEEK_END);
//将偏移量修改到文件size/2
lseek(fd_r, size/2, SEEK_SET);
lseek(fd_w, size/2, SEEK_SET);
//循环拷贝前半部分
char c = 0;
for(int i=size/2; i<size; i++)
{
read(fd_r, &c, 1);
write(fd_w, &c , 1);
}
printf("后半部分拷贝完毕\n");
close(fd_r);
close(fd_w);
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
int fd_w = open("./2.png", O_WRONLY|O_CREAT|O_TRUNC, 0664);
if(fd_w < 0)
{
perror("open");
return -1;
}
close(fd_w);
pthread_t tid1, tid2;
if(pthread_create(&tid1, NULL, callBack1, NULL) != 0)
{
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
return -1;
}
if(pthread_create(&tid2, NULL, callBack2, NULL) != 0)
{
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
return -1;
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
3.2 信号量(又称为:信号灯)
3.2.1 工作原理
-
对于要访问临界资源的线程,都去执行申请信号量的操作。
-
- 当信号量的值大于0,则申请成功,信号量的值-1;
-
- 当信号量的值等于0,则申请失败,该申请操作会阻塞,线程进入休眠,等待信号量的值大于0;
-
互斥锁又称之为二值信号量,只允许一个线程进入临界区,即信号量的初始值为1;
-
信号量根据初始值不同,可以让一个或者多个线程同时进入临界区。例如初始值为2,则允许两个线程同时进入临界区。
-
PV操作:实现线程、进程同步互斥的有效方式
-
- P操作:申请信号量,减操作 -1
-
- V操作:释放信号量,加操作 +1
3.2.2 sem_init(创建并初始化信号量)
功能:创建并初始化信号灯
原型:
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
-
sem_t *sem:该指针指向的内存空间中会存储创建好的信号灯;
-
int pshared:共享标识,用于指定是用在线程之间还是进程之间;
-
0:用于线程
-
非0: 用于进程;
-
-
unsigned int value:指定信号灯的初始值;
返回值:
- 成功,返回0;
- 失败,返回-1,更新errno;
3.2.4 sem_wait(P操作,申请信号量)
功能:对信号量做P操作 -1操作(申请信号量)
- 当信号量中的值大于0,则申请信号量成功,线程进入临界区。
- 当信号量中的值等于0,则申请信号量失败,线程进入休眠阻塞阶段,直到信号量的值大于0,解除阻塞。
原型:
#include <semaphore.h>
int sem_wait(sem_t *sem);
返回值:
- 成功,返回0;
- 失败,返回-1,更新errno;
3.2.5 sem_post(V操作,释放信号量)
功能:对指定信号量做V操作;(释放信号量)
原型:
#include <semaphore.h>
int sem_post(sem_t *sem);
返回值:
- 成功,返回0;
- 失败,返回-1,更新errno;
3.2.6 sem_destroy(销毁信号量)
功能:销毁信号量
原型:
#include <semaphore.h>
int sem_destroy(sem_t *sem);
参数:
- sem_t *sem: 信号量指针
返回值:
- 成功,返回0;
- 失败,返回-1,更新errno;
3.2.7 信号量示例代码
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>
//临界资源
char buf[] = "AAAA____BBBB";
//信号量
sem_t sem;
void* callback1(void* arg)
{
while(1)
{
/******临界区************/
//P操作
if(sem_wait(&sem) < 0)
{
perror("sem_wait");
break;
}
printf("%s\n", buf);
//V操作
if(sem_post(&sem) < 0)
{
perror("sem_post");
break;
}
/******临界区************/
}
pthread_exit(NULL);
}
void* callback2(void* arg)
{
char temp = 0;
while(1)
{
/******临界区************/
//P操作
if(sem_wait(&sem) < 0)
{
perror("sem_wait");
break;
}
for(int i=0; i<strlen(buf)/2; i++)
{
temp = buf[i];
buf[i] = buf[strlen(buf)-1-i];
buf[strlen(buf)-1-i] = temp;
}
//V操作
if(sem_post(&sem) < 0)
{
perror("sem_post");
break;
}
/******临界区************/
}
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
//创建信号量
if(sem_init(&sem, 0, 1) < 0)
{
perror("sem_init");
return -1;
}
pthread_t tid1, tid2;
//创建线程:打印
if(pthread_create(&tid1, NULL, callback1, NULL) != 0)
{
fprintf(stderr, "pthread_create failed\n");
return -1;
}
pthread_detach(tid1);
//创建线程:倒置
if(pthread_create(&tid2, NULL, callback2, NULL) != 0)
{
fprintf(stderr, "pthread_create failed\n");
return -1;
}
pthread_join(tid2, NULL);
//销毁信号量
sem_destroy(&sem);
return 0;
}
3.3 条件变量
3.3.1 工作原理
- 将不访问共享资源的线程直接休眠,并设置一个唤醒条件,该唤醒条件称之为条件变量;
- 当到线程需要访问的时候,其他线程通过指定的条件变量唤醒该线程。
3.3.2 pthread_cond_init(创建并初始化信号量)
功能:创建并初始化条件变量;
原型:
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
参数:
- pthread_cond_t *cond:该指针指向的内存空间中,存储申请后的条件变量;
- const pthread_condattr_t *condattr:条件变量属性,该属性可以指定条件变量是用于进程还是用于线程的同步互斥。 填NULL代表用于线程.。
返回值:
- 成功,返回0;
- 失败,返回非0;
3.3.3 pthread_cond_destroy(销毁条件变量)
功能: 销毁条件变量
原型:
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_destroy(pthread_cond_t *cond);
参数:
- pthread_cond_t *cond
返回值:
3.3.4 pthread_cond_wait(设置一个唤醒条件, 解开互斥锁,休眠等待被唤醒)
功能:设置一个唤醒条件, 解开互斥锁,休眠等待被唤醒
- 当其他线程用pthread_cond_signal唤醒后,该函数尝试上锁
- 如果上锁成功,则唤醒成功 ,当成功被唤醒后,会从当前位置继续往后执行
- 如果上锁失败,则重新回到cond上继续休眠
原型:
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
参数:
-
pthread_cond_t *cond:指定唤醒条件,即指定条件变量; 一般称线程睡在该条件变量上;
-
pthread_mutex_t *mutex:指定要解开的互斥锁;
返回值:
-
成功,返回0;
-
失败,返回非0;
注意:
-
pthread_cond_wait会将线程放入cond_wait队列,解开互斥锁,等待被唤醒。(原子操作:即cpu资源不会被切走的操作)
-
pthread_cond_signal函数,会将线程从cond_wait队列切换到mutex_wait队列中。同时是signal计数器+1;
-
线程会在mutex_wait队列中等待锁资源。
-
当成功获取到锁资源后,线程会成功被唤醒,此时signal计数器-1;
-
若mutex_wait队列中有多个线程,则没有获取到锁资源的线程会重新回到cond_wait队列上继续休眠,等待下一次唤醒。
ps:如果上述不能理解,则简化版:pthread_cond_signal肯定会随机唤醒一个睡在cond上的线程。
条件变量与互斥锁共用的原因
- pthread_cond_init创建的条件变量本质上是在内核中创建一个cond_wait 队列,凡是用pthread_cond_wait函数睡在cond上的条件变量,均会被添加到cond_wait队列中。
- 则有可能会出现,线程A调用pthread_cond_wait函数的时候,还没有运行到添加到队列中时,时间片结束。线程B调用pthread_cond_signal函数,导致漏唤醒。
- 所以加上互斥锁机制,在pthread_cond_wait中传入互斥锁,当pthread_cond_wait函数将线程A添加到队列后,再解开互斥锁。允许去执行pthread_cond_signal函数。
3.3.5 pthread_cond_signal(随机唤醒线程)
功能:通过指定条件变量,随机唤醒一个睡在指定条件变量上的线程;
原型:
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
参数:
- pthread_cond_t *cond:唤醒指定条件变量上的线程;
返回值:
- 成功,返回0;
- 失败,返回非0;
3.3.6 pthread_cond_broadcast(唤醒所有线程)
功能: 唤醒所有睡在cond上的线程
原型:
int pthread_cond_broadcast(pthread_cond_t *cond);
参数:
- pthread_cond_t *cond:唤醒指定条件变量上的线程;
3.3.7 条件变量示例代码
#include <stdio.h>
#include <pthread.h>
#include <string.h>
//临界资源
char buf[] = "1234567";
//互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int flag = 0; //0打印 1倒置
void* callBack_print(void* arg)
{
while(1)
{
/***********临界区***********/
pthread_mutex_lock(&mutex); //上锁
//当不是当前线程要访问的时机,则让当前线程休眠
if(0 != flag)
{
//设置条件变量(唤醒条件),同时解开互斥锁
//休眠,等待被唤醒
pthread_cond_wait(&cond, &mutex);
//尝试上锁,如果上锁成功,则唤醒成功
//当成功被唤醒后,会从当前位置继续往后执行
//如果上锁失败,则重新回到cond上继续休眠
}
printf("%s\n", buf);
flag = 1; //修改运行时机,设置为非0
//通过指定条件变量唤醒指定线程
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex); //解锁
/***********临界区***********/
}
pthread_exit(NULL);
}
void* callBack_swap(void* arg)
{
char* start, *end;
char temp;
while(1)
{
/***********临界区***********/
pthread_mutex_lock(&mutex); //上锁
//当不是当前线程要访问的时机,则让当前线程休眠
if(1 != flag)
{
//设置条件变量(唤醒条件),同时解开互斥锁
//休眠,等待被唤醒
pthread_cond_wait(&cond, &mutex);
//尝试上锁,如果上锁成功,则唤醒成功
//当成功被唤醒后,会从当前位置继续往后执行
//如果上锁失败,则重新回到cond上继续休眠
}
start = buf;
end = buf+strlen(buf)-1;
while(start < end)
{
temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
flag = 0;
//通过指定条件变量唤醒指定线程
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex); //解锁
/***********临界区***********/
}
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
//创建互斥锁 方式一
//pthread_mutex_init(&mutex, NULL);
//创建条件变量 方式一
/*
if(pthread_cond_init(&cond, NULL) != 0)
{
fprintf(stderr, "pthread_cond_init failed __%d__\n", __LINE__);
return -1;
}
*/
pthread_t tid_p, tid_s;
if(pthread_create(&tid_p, NULL, callBack_print, NULL) != 0)
{
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
return -1;
}
pthread_detach(tid_p); //分离线程
if(pthread_create(&tid_s, NULL, callBack_swap, NULL) != 0)
{
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
return -1;
}
pthread_join(tid_s, NULL); //阻塞等待tid_s线程退出
pthread_mutex_destroy(&mutex); //销毁互斥锁
pthread_cond_destroy(&cond); //销毁条件变量
return 0;
}
}
flag = 0;
//通过指定条件变量唤醒指定线程
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex); //解锁
/***********临界区***********/
}
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
//创建互斥锁 方式一
//pthread_mutex_init(&mutex, NULL);
//创建条件变量 方式一
/*
if(pthread_cond_init(&cond, NULL) != 0)
{
fprintf(stderr, "pthread_cond_init failed __%d__\n", __LINE__);
return -1;
}
*/
pthread_t tid_p, tid_s;
if(pthread_create(&tid_p, NULL, callBack_print, NULL) != 0)
{
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
return -1;
}
pthread_detach(tid_p); //分离线程
if(pthread_create(&tid_s, NULL, callBack_swap, NULL) != 0)
{
fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
return -1;
}
pthread_join(tid_s, NULL); //阻塞等待tid_s线程退出
pthread_mutex_destroy(&mutex); //销毁互斥锁
pthread_cond_destroy(&cond); //销毁条件变量
return 0;
}