线程概念

什么是线程

LWP:light weight process 轻量级的进程,本质仍是进程(在Linux环境下)
进程:独立地址空间,拥有PCB(进程控制块PCB(Process Control Block)
线程:也有PCB,但没有独立的地址空间(共享)
区别:在于是否共享地址空间。 独居(进程);合租(线程)。
Linux下: 线程:最小的执行单位,调度的基本单位。
进程:最小分配资源单位,可看成是只有一个线程的进程。
在linux下,线程最是小的执行单位;进程是最小的分配资源单位

线程共享资源

1.文件描述符表
2.每种信号的处理方式
3.当前工作目录
4.用户ID和组ID
5.内存地址空间 (.text/.data/.bss/heap/共享库)

线程非共享资源

1.线程id
2.处理器现场和栈指针(内核栈)
3.独立的栈空间(用户空间栈)
4.errno变量
5.信号屏蔽字
6.调度优先级

线程优、缺点

优点:
1、提高程序并发性
2、开销小
3、数据通信、共享数据方便
缺点:
1、库函数,不稳定
2、调试、编写困难、gdb不支持
3、对信号支持不好
优点相对突出,缺点均不是硬伤。Linux下由于实现方法导致进程、线程差别不是很大。

线程相关操作函数


数据类型转换

1、void *arg---->int i=0;
i=*( (int *)arg );
int i=1;---->void *arg
&i-->int * (&i)===>i;
pthread_create(&thread, NULL, start_routine, (void *)&i)
2、int *ret[i];--->int
ret[i]-->int *---->int *p中的p(地址)
*ret[i]----》int
3、int --->void *
int var;
pthread_exit(void *arg)
pthread_exit((void *)&var);4、int *ret[i];----》void **类型
ret[i]--->int *类型
&ret[i]--》int类型(void **)ret[i]---》void **类型int pthread_join(pthread_t thread, void **retval);
pthread_join(tid[i], (void **)&ret[i])
//函数指针;void *(*start_routine) (void *)
//指针函数: void *start_routine (void *arg)

创建线程程序例程1:

//错误判断--》等价于if(){perror(“”);return -1}
#define handle_error_en(en, msg) \do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
int *p=NULL;
//清理函数:
void routine(void *arg)
{free(p);printf("this is routine free\n");
}
//子线程
void *start_routine (void *arg)
{int i=0;static int stat=1;int oldstate=0;int oldtype=0;//pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);/*PTHREAD_CANCEL_DISABLE    //不会被取消在使用  PTHREAD_CANCEL_ENABLE前提下:pthread_setcanceltype(参数设置为:)PTHREAD_CANCEL_DEFERRED//延時取消(该线程开始并不取消;等到下个取消点到来结束线程) PTHREAD_CANCEL_ASYNCHRONOUS//立即取消*/pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);//可以取消pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);//立即取消//入栈 ---》清除资源成对出现pthread_cleanup_push(routine,NULL);p=(int *)malloc(sizeof(int));for(i=0;i<5;i++){printf("this is my first pthread,the id is%lu\n",pthread_self());sleep(1);}free(p);printf("free success\n");//出栈 pthread_cleanup_pop(1);//退出子线程;返回子线程状态:statpthread_exit((void *)&stat);
}
int main(int argc, char const* argv[])
{int i=0;pthread_attr_t attr;pthread_t thread=0;int num=10;void *stat=(void *)0;/*1.设置属性---》初始化 attr:线程属性 pthread_attr_init(&attr);2.设置线程分离属性 : int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);PTHREAD_CREATE_DETACHED:分离PTHREAD _CREATE_JOINABLE:非分离3.设置绑定属性 :int pthread_attr_setscope(pthread_attr_t *attr, int scope);PTHREAD_SCOPE_SYSTEM:绑定PTHREAD_SCOPE_PROCESS:非绑定 */
//分离,非绑定 pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);pthread_attr_setscope(&attr,PTHREAD_SCOPE_PROCESS);//创建子线程 int ret=pthread_create(&thread,&attr,start_routine,(void *)&num);if(ret!=0){handle_error_en(ret,"pthread_create");}for(i=0;i<5;i++){printf("this is main thread,son id is %lu\n",thread);if(i==2)//终止线程 pthread_cancel(thread);sleep(1);}/*若使用下面语句--》运行报错--》核心已转储--》原因: pthread_exit((void *)&stat)还未将数值传入stsat就被终止----》pthread_join(thread,(void *)&stat);获取不到子线程状态*///pthread_detach(thread);//pthread_join(thread,(void *)&stat);//printf("%d\n",*(int *)stat);pthread_join(thread,NULL);return 0;
}

创建线程程序例程2:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>//var:全局变量----》子线程状态
int var = 100;
//全局变量---》在子线程中开辟空间但未 释放
int *p=NULL;
void routine(void *arg)
{free(p);printf("this is routine free\n");
}//创建的5个子线程
void *tfn(void *arg)
{//入栈 pthread_cleanup_push(routine,NULL);p=(int *)malloc(sizeof(int));free(p);//出栈 pthread_cleanup_pop(1);printf("free success\n");int i;i = (int)arg;sleep(i);//线程1: i为函数传参 if (i == 1) {var = 1;printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i, pthread_self(), var);//返回线程1状态 ---同 pthread_exit((void *)var);return (void *)&var;}//线程3:else if (i == 3) {printf("hello\n"); var = 3;printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i, pthread_self(), var);//返回线程3状态pthread_exit((void *)&var);} // 线程2/4/5 else  {printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i, pthread_self(), var);pthread_exit((void *)&var);}return NULL;
}//主线程
int main(void)
{//创建多个线程----》数组保存多个线程ID pthread_t tid[5];int i;//数组保存接受的多个线程状态:int *ret[5];  //创建5个线程 第一个参数:pthread_t *thread; 第三个参数:子线程函数 for (i = 0; i < 5; i++)pthread_create(&tid[i], NULL, tfn, &i);//分别获取子线程(0;1;2;3;4)状态 for (i = 0; i < 5; i++) {if (i!=1){//除线程2;其余进程全部解除与主进程关联 pthread_detach(tid[i]);//取消i==3(第四个进程) if (i==3){pthread_cancel(tid[i]);  //自己添加取消点*/}}//第二个线程(i==1时进入else) else {//阻塞父线程;等待子线程退出,获取线程退出状态pthread_join(tid[i], (void **)&ret[i]);//打印出子线程状态 printf("-------%d 's ret = %d\n", i, (int)ret[i]);  }}printf("I'm main pthread tid = %lu\t var = %d\n", pthread_self(), var);sleep(i);return 0;
}

线程私有数据 (不同线程对同一键值取不同的值)

线程私有数据 (Thread-specific Data,或称为 TSD):
线程私有数据采用一键多值的技术,即一个键对应多个数值。
通过键值来访问数据,看似对一个变量进行访问,其实是在访问不同的数据。使用线程私有数据时,首先要为每个线程数据创建一个相关联的键。在各个线程内部,都使用这个公用的键来指代线程数据,但是在不同的线程中,这个键代表的数据是不同的。
线程私有数据采用了一种被称为一键多值的技术,即一个键对应多个数值

线程私有数据相关API
相关操作:

下面接口所需头文件:
#include <pthread.h>
1)创建线程私有数据
2)注销线程私有数据
3)设置线程私有数据的关联
4)读取线程私有数据所关联的值

程序例程:

/*
程序运行结果
I'm 1th pthread, id = 139960459020032  var = 1    setspecific_value=111
FUN1第1个子线程返回给主线程状态为1线程4读取私有数据为:0
I'm 4th pthread, id = 139960433841920  var = 4    setspecific_value=444
FUN1第4个子线程返回给主线程状态为4*/#include "stdio.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>//var:全局变量----》子线程状态
int var = 100;
//创建私有数据 ----》线程创建:
pthread_key_t key;
//线程1私有数据清理函数1: void fun1 (void *arg){printf("FUN1\n");} //线程2私有数据清理函数2: void fun2 (void *arg){printf("FUN2\n");} //创建的5个子线程
void *tfn(void *arg)
{int i;i = (int)arg;//不同的睡眠时间--》运行结果不同??????sleep(i);//线程1: i为函数传参 if (i == 1) {int value=111;var = 1;//2----设置线程私有数据的关联pthread_setspecific(key, &value);printf("I'm %dth pthread, id = %lu\t var = %d\t setspecific_value=%d\n", i, pthread_self(), var,*((int *)pthread_getspecific(key)));//返回线程1状态 ---同 pthread_exit((void *)&var);pthread_exit((void *)&var);}//线程3:else if (i == 3) {var = 3;printf("I'm %dth pthread, pthread_id = %lu\t var = %d\n", i, pthread_self(), var);//返回线程3状态pthread_exit((void *)var);} //线程4:读取线程私有数据所关联的值else if (i== 4) {int value=444;var = 4;pthread_setspecific(key, &value); printf("I'm %dth pthread, id = %lu\t var = %d\t setspecific_value=%d\n", i, pthread_self(), var,*((int *)pthread_getspecific(key)));//返回线程3状态pthread_exit((void *)var);}//    线程2/4/5 else  {sleep(1);printf("I'm %dth pthread, pthread_id = %lu\t var = %d\n", i, pthread_self(), var);pthread_exit((void *)var);}return NULL;
}**//主线程**
int main(void)
{//创建多个线程----》数组保存多个线程ID pthread_t tid[5];int i;//数组保存接受的多个线程状态:int *ret[5];
//主函数------------------------------------------创建私有数据: pthread_key_create(&key, fun1);//创建5个线程 第一个参数:pthread_t *thread; 第三个参数:子线程函数 for (i = 0; i < 5; i++)pthread_create(&tid[i], NULL, tfn, (void *)i);//分别获取子线程(0;1;2;3;4)状态 for (i = 0; i < 5; i++) {//取消i==3(第四个进程) if ( (i==3)||(i==0) ){pthread_cancel(tid[i]);    //自己添加取消点*/sleep(1);}if (i==2){pthread_cancel(tid[i]);    //自己添加取消点*/sleep(1);}//第二个线程(i==1时进入else) else if ( (i==1)||(i==4) ){//阻塞父线程;等待子线程退出,获取线程退出状态pthread_join(tid[i], (void **)&ret[i]);//打印出子线程状态 printf(" 第%d个子线程返回给主线程状态为%d\n", i, (int)ret[i]); }}sleep(i);return 0;
}

线程间的同步和互斥

互斥信号量(互斥锁)与信号量
1、为什么进行同步以及互斥:实现对资源的保护、独占

互斥锁的作用
保护共享数据: 在并发机制的情况下,有时候会有多个线程同时访问同一片数据,为了保护数据操作的准确性就需要通过加锁来进行保护。
保持操作互斥: 可能一个程序会有多个操作,但是同一个时间只能有一个操作被执行,例如a/b两个操作,如果a被执行,b就不能被执行,同理b被执行,a就不能执行操作函数
互斥锁分类
1、快速互斥锁
最常用的锁,符合以上的含义
2、检测锁
快速互斥锁的非阻塞版本
3、递归锁
多次加锁

**

互斥锁相关函数:

**

互斥锁相关操作函数:
1、pthread_mutex_t lock; /* 互斥锁定义 */
2、pthread_mutex_init(&lock, NULL); /* 动态初始化,成功返回0,失败返回非0 */
3、pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; /* 静态初始化 */
4、pthread_mutex_lock(&lock); /* 互斥锁上锁;阻塞的锁定互斥锁
5、pthread_mutex_trylock(&thread_mutex);/* 互斥锁判断上锁;非阻塞的锁定互斥锁,成功获得互斥锁返回0,如果未能获得互斥锁,立即返回一个错误码
6、pthread_mutex_unlock(&lock); /* 解锁互斥锁/
7、pthread_mutex_destroy(&lock) /* 销毁互斥锁 */

例程1: 互斥锁保护共享数据

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
//全局变量:
static pthread_mutex_t g_mutex_lock;
static int g_count = 0;static void *thread_fun_1(void *data)
{//加锁pthread_mutex_lock(&g_mutex_lock);g_count++;printf("%s g_count: %d\n", __func__, g_count);//解锁pthread_mutex_unlock(&g_mutex_lock);
}static void *thread_fun_2(void *data)
{pthread_mutex_lock(&g_mutex_lock);g_count++;printf("%s g_count: %d\n", __func__, g_count);pthread_mutex_unlock(&g_mutex_lock);
}static void *thread_fun_3(void *data)
{pthread_mutex_lock(&g_mutex_lock);g_count++;printf("%s g_count: %d\n", __func__, g_count);pthread_mutex_unlock(&g_mutex_lock);
}int main(int argc, char const *argv[])
{int ret;pthread_t pid[3];//主线程初始化互斥锁ret = pthread_mutex_init(&g_mutex_lock, NULL);if (ret != 0) {printf("mutex init failed\n");return -1;}//创建3个子线程:pthread_create(&pid[0], NULL, thread_fun_1, NULL);pthread_create(&pid[1], NULL, thread_fun_2, NULL);pthread_create(&pid[2], NULL, thread_fun_3, NULL);//等待线程结束---阻塞主线程pthread_join(pid[0], NULL);pthread_join(pid[1], NULL);pthread_join(pid[2], NULL);//删除互斥锁:pthread_mutex_destroy(&g_mutex_lock);return 0;
}
/*
加互斥锁:
对数据g_count的操作进行加锁之后,同一个时间只有一个线程能获取到锁,也就是只有一个线程能对g_count进行操作,保证了g_count的数据的准确性打印结果:各进程依次运行
thread_fun_1 g_count: 1
thread_fun_2 g_count: 2
thread_fun_3 g_count: 3若不加互斥锁:
3个线程都会对g_count进行操作,没有任何保护的情况下,3个线程是存在竞争关系的,所以g_count最终可能会是1、2、3三种值中的一种,*/

实例2:保持操作的互斥性

有些情况下,2个不同的操作是不能同时进行的,例如fingerprint中的enroll和verify同一时间只能有一个操作进行。保持操作的互斥性本质上其实还是在保护共有的数据。下面的例子,打印hello的时候,world是无法打印的,如果希望打印world只能等待打印hello的线程退出之后再打印

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>static pthread_mutex_t g_mutex_lock;static void *thread_fun_1(void *data)
{//阻塞锁:直到获取到锁才结束运行:阻塞其他线程pthread_mutex_lock(&g_mutex_lock);int i = 0;while (i < 5) {printf("hello\n");i++;sleep(1);}pthread_mutex_unlock(&g_mutex_lock);
}static void *thread_fun_2(void *data)
{pthread_mutex_lock(&g_mutex_lock);int i = 0;while (i < 5) {printf("world\n");i++;sleep(1);}pthread_mutex_unlock(&g_mutex_lock);
}static void do_print_hello()
{pthread_t pth_id;int result = pthread_create(&pth_id, NULL, thread_fun_1, NULL);
}static void do_print_world()
{pthread_t pth_id;int result = pthread_create(&pth_id, NULL, thread_fun_2, NULL);
}int main(int argc, char const *argv[])
{int ret;int cid;ret = pthread_mutex_init(&g_mutex_lock, NULL);if (ret != 0) {printf("mutex init failed\n");return -1;}printf("0---------------hello\n");printf("1---------------world\n");printf("2---------------EXIT\n");while (1) {printf("input a num: \n");scanf("%d", &cid);getchar();switch (cid) {case 0:do_print_hello();break;case 1:do_print_world();break;case 2:pthread_mutex_destroy(&g_mutex_lock);return 0;default:  break;}}}

非阻塞式的锁

上面的互斥锁是阻塞式的锁,也可以通过非阻塞式的锁进行,看下面的例子,pthread_mutex_trylock()函数如果获取到互斥锁了,会返回0,如果没有获取的互斥锁,会立即返回一个非0值,例子中通过g_cancel来通知线程进行退出,如果当前正在打印hello,发出打印world命令之后,通过pthread_mutex_trylock()就能知道当前有没有打印线程正在运行,如果有在运行的线程,通过置位g_cancel来退出正在运行的线程

/*
现象:
当按下打印hello命令时;打印hello;
再次按下打印hello命令会停止打印;按下打印world命令时打印world
world同Hello

原理:
定义全局变量:打印的标志位
针对于打印hello函数:
按下1命令------》开启线程1进入while中先对标志位进行检测;满足条件–》进行打印hello数据
再次按下1/0-----》又打开一个线程2;对标志位进行设置;—》导致线程1检测到标志改变打印停止
*/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>static pthread_mutex_t g_mutex_lock;
//打印状态标志位:
static int g_cancel = 0;static void *thread_fun_1(void *data)
{printf("hello_id :%lu\n",pthread_self());
//非阻塞锁:检测不到锁直接返回0---》打印状态的全局变量值改变if (pthread_mutex_trylock(&g_mutex_lock) != 0) {printf("11111\n");g_cancel = 1;return 0;}int i = 0;g_cancel = 0;while (i < 5) {//其他线程对全局变量打印状态的值改变时;该线程停止打印if (g_cancel) break;printf("hello\n");i++;sleep(1);}pthread_mutex_unlock(&g_mutex_lock);
}static void *thread_fun_2(void *data)
{printf("world_id :%lu\n",pthread_self());if (pthread_mutex_trylock(&g_mutex_lock) != 0) {printf("2222\n");g_cancel = 1;return 0;}int i = 0;g_cancel = 0;while (i < 5) {if (g_cancel) break;printf("world\n");i++;sleep(1);}pthread_mutex_unlock(&g_mutex_lock);
}static void do_print_hello()
{pthread_t pth_id;int result = pthread_create(&pth_id, NULL, thread_fun_1, NULL);}static void do_print_world()
{pthread_t pth_id;int result = pthread_create(&pth_id, NULL, thread_fun_2, NULL);}int main(int argc, char const *argv[])
{int ret;int cid;ret = pthread_mutex_init(&g_mutex_lock, NULL);if (ret != 0) {printf("mutex init failed\n");return -1;}printf("0---------------hello\n");printf("1---------------world\n");printf("2---------------EXIT\n");while (1) {printf("input a num: \n");scanf("%d", &cid);getchar();switch (cid) {case 0:do_print_hello();break;case 1:do_print_world();break;case 2:pthread_mutex_destroy(&g_mutex_lock);return 0;default:break;}}
}

Linux线程的信号量同步

信号量和互斥锁(mutex)的区别:
互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。
信号量相关操作函数:

头文件semaphore.h。
主要用到的函数:
·int sem_init(sem_t *sem, int pshared, unsigned int value);
·sem是要初始化的信号量,
·pshared表示此信号量是在进程间共享还是线程间共享
·value是信号量的初始值。
·int sem_destroy(sem_t *sem)  ----》销毁的信号量
·sem是要销毁的信号量。只有用sem_init初始化的信号量才能用sem_destroy销毁。
·int sem_wait(sem_t *sem);     -----》等待信号量,
·如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞。相当于P操作。成功返回0,失败返回-1。
 ·int sem_post(sem_t *sem); 释放信号量,
·让信号量的值加1。相当于V操作。

下列的代码演示了如何用信号量同步,模拟一个窗口服务系统。

//@purpose: 基于信号量的多线程同步,操作系统原理中的P,V操作
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define CUSTOMER_NUM 10
/* @Scene: 某行业营业厅同时只能服务两个顾客。* 有多个顾客到来,每个顾客如果发现服务窗口已满,就等待,* 如果有可用的服务窗口,就接受服务。 */
/* 将信号量定义为全局变量,方便多个线程共享 */
sem_t sem;
/* 每个线程要运行的例程 */
void * get_service(void *thread_id)
{/* 注意:立即保存thread_id的值,因为thread_id是对主线程中循环变量i的引用,它可能马上被修改 */
int customer_id = *((int *)thread_id);
//信号量大于0--》进行P操作(信号量-1操作)if(sem_wait(&sem) == 0){//usleep(100);                /* service time: 100ms */sleep(1);printf("customer %d receive service ...\n", customer_id);//进行V操作(信号量+1)sem_post(&sem);}
}int main(int argc, char *argv[])
{/* 初始化信号量,初始值为2,表示有两个顾客可以同时接收服务 *//* @prototype: int sem_init(sem_t *sem, int pshared, unsigned int value); *//* pshared: if pshared == 0, the semaphore is shared among threads of a process* otherwise the semaphore is shared between processes.   */sem_init(&sem, 0, 2);/* 为每个顾客定义一个线程id, pthread_t 其实是unsigned long int */pthread_t customers[CUSTOMER_NUM];int i, ret;/* 为每个顾客生成一个线程 */for(i = 0; i < CUSTOMER_NUM; i++){int customer_id = i;ret = pthread_create(&customers[i], NULL, get_service, &customer_id);if(ret != 0){perror("pthread_create");exit(1);}else{printf("Customer %d arrived.\n", i);}usleep(10);}/* 等待所有顾客的线程结束 *//* 注意:这地方不能再用i做循环变量,因为可能线程中正在访问i的值 */int j;for(j = 0; j < CUSTOMER_NUM; j++) {pthread_join(customers[j], NULL);}/* Only a  semaphore that  has been initialized  by sem_init(3)* should be destroyed using sem_destroy().*/sem_destroy(&sem);return 0;
}

编译:gcc sem.c -lpthread

运行结果(注意,每次运行都不相同):

/*
实验现象:窗口每次只接受2人
Customer 0 arrived.
Customer 1 arrived.
Customer 2 arrived.
Customer 3 arrived.
Customer 4 arrived.
Customer 5 arrived.
Customer 6 arrived.
Customer 7 arrived.
Customer 8 arrived.
Customer 9 arrived.
customer 0 receive service ...
customer 1 receive service ...
customer 3 receive service ...
customer 2 receive service ...
customer 4 receive service ...
customer 5 receive service ...
customer 6 receive service ...
customer 7 receive service ...
customer 8 receive service ...
customer 9 receive service ...
*/

linux中的条件变量的使用

什么是条件变量
条件变量是利用线程间共享的全局变量进行同步的一种机制。
主要包括两个动作:一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立”(给出条件成立信号)。
为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
条件变量类型为 pthread_cond_t。
条件变量有什么用
使用条件变量可以以原子方式阻塞线程,直到某个特定条件为真为止。条件变量始终与互斥锁一起使用,对条件的测试是在互斥锁(互斥)的保护下进行的。
如果条件为假,线程通常会基于条件变量阻塞,并以原子方式释放等待条件变化的互斥锁。如果另一个线程更改了条件,该线程可能会向相关的条件变量发出信号,从而使一个或多个等待的线程执行以下操作:

程序实例

条件变量的使用可以分为两部分:
等待线程
使用pthread_cond_wait前要先加锁;
pthread_cond_wait内部会解锁,然后等待条件变量被其它线程激活;
pthread_cond_wait被激活后会再自动加锁;
激活线程:
加锁(和等待线程用同一个锁);
pthread_cond_signal发送信号;
解锁;
激活线程的上面三个操作在运行时间上都在等待线程的pthread_cond_wait函数内部

/
程序执行结果:
decrement lock
incremen lock
进程2释放条件信号
进程1被唤醒
/

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
//互斥锁变量定义
pthread_mutex_t count_lock;
//条件变量标志符定义
pthread_cond_t count_nonzero;
unsigned count = 0;
//子线程:
void *decrement_count(void *arg)
{//加锁pthread_mutex_lock(&count_lock);printf("decrement  lock\n");while(count == 0){printf("decrement_count count == 0 \n");// 用于阻塞当前线程,等待别的线程使用 pthread_cond_signal() 或 pthread_cond_broadcast来唤醒它 // pthread_cond_wait()通过(返回)时,该线程又自动获得该 mutex 。pthread_cond_wait(&count_nonzero, &count_lock);printf("进程1被唤醒 \n");}count = count + 1;pthread_mutex_unlock(&count_lock);
}void *increment_count(void *arg)
{pthread_mutex_lock(&count_lock);printf("increment_count lock \n");if(count == 0){//发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.//如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。pthread_cond_signal(&count_nonzero);printf("进程2释放条件信号 \n");}count = count + 1;pthread_mutex_unlock(&count_lock);
}int main(void)
{pthread_t tid1, tid2;//互斥锁与条件变量初始化:pthread_mutex_init(&count_lock, NULL);pthread_cond_init(&count_nonzero, NULL);//创建线程1:pthread_create(&tid1, NULL, decrement_count, NULL);sleep(2);//创建线程2:pthread_create(&tid2, NULL, increment_count, NULL);sleep(3);//等待线程结束pthread_exit(0);return 0;
}
  • TEACHER 遗留问题: 子线程等待父线程发送信号被唤醒—》成功 父线程等待子线程发送信号唤醒----》失败
#if 1#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define handle_error_en(en, msg) \do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
//线程私有数据
pthread_key_t key;
//线程互斥锁
pthread_mutex_t  mutex;
//线程条件变量
pthread_cond_t cond;//
void destr_function (void *arg)
{printf("this is fun2\n");
}
//子线程
void *starts_routine (void *arg)
{int i=0,num=20,std=0;static int stat=0,err=1;//设置私有数据pthread_setspecific(key,&num);//子线程上锁pthread_mutex_lock(&mutex);#if 1//条件变量无条件等待pthread_cond_wait(&cond,&mutex);printf("进程1被唤醒 \n");#endiffor(i=0;i<5;i++){//读取私有数据std=*(int *)pthread_getspecific(key);printf("this is my second pthread,the std is %d\n",std);sleep(1);}//解锁pthread_mutex_unlock(&mutex);//等待子线程退出;返回子线程状态:statpthread_exit((void *)&stat);
}
int main(int argc, char const* argv[])
{int i=0;int num=10;void *stat=(void *)0;pthread_t thread=0,thread1=0;//互斥锁初始化pthread_mutex_init(&mutex,0);//条件变量初始化pthread_cond_init(&cond, NULL);//创建线程私有数据;清理函数:destr_functionpthread_key_create(&key, destr_function);//创建子线程starts_routine;传入参数:numint ret1=pthread_create(&thread1,NULL,starts_routine,(void *)&num);if(ret1!=0){handle_error_en(ret1,"pthread_create");}sleep(1);//主线程上锁pthread_mutex_lock(&mutex);printf("主进程被唤醒 \n");for(i=0;i<5;i++){printf("this is main thread,son id is %lu\n",thread);if(i==2){//唤醒一个等待条件的线程pthread_cond_signal(&cond);printf("主線程释放条件信号 \n");}    }//主线程开锁pthread_mutex_unlock(&mutex);//等待子线程结束pthread_join(thread,NULL);pthread_join(thread1,NULL);//删除私有数据pthread_key_delete(key);return 0;
}
#endif

LINUX线程及线程间通信相关推荐

  1. linux线程间通信优点,进程间通信与线程间通信【转】

    一个进程写管道:写入字节数小于PIPE_BUF是原子操作,写操作在管道缓冲区没有及时读走时发生阻塞. 一个进程读管道:读操作在管道缓冲区没有数据时发生阻塞. 以前一直想找个机会总结一下进程和线程的通信 ...

  2. Unix/Linux IPC及线程间通信总结

    一.互斥与同步 1.互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性.但互斥无法限制访问者对资源的访问顺序,即访问是无序的. 2.同步:是指在互斥的基础上(大多数情况),通过其它 ...

  3. linux系统线程通信的几种方式,Linux进程间通信-线程间通信

    Linux作为一种新兴的操作系统,几乎支持所有的Unix下常用的进程间通信方法:管道.消息队列.共享内存.信号量.套接口. 1.管道 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动 ...

  4. 基于Linux的内存模拟型的字符IO设备驱动程序的设计,并实现线程间通信

    要求:基于Linux的内存模拟型的字符IO设备驱动程序的设计,并编写应用程序实现线程间的通信. 基于Linux的内存模拟型的字符IO设备驱动程序的设计 1 内存模拟型的字符IO设备驱动程序memDrv ...

  5. linux高级编程基础系列:线程间通信

    线程间通信机制: 线程是一种轻量级的进程. 进程的通信机制主要包括无名管道.有名管道.消息队列.信号量.共享内存以及信号等.这些机制都是由linux内核来维护的,实现起来都比较复杂,而且占用大量的系统 ...

  6. Linux的进程/线程间通信方式总结

    Linux系统中的进程间通信方式主要以下几种: 同一主机上的进程通信方式 * UNIX进程间通信方式: 包括管道(PIPE), 有名管道(FIFO), 和信号(Signal) * System V进程 ...

  7. 进程间通信线程间通信

    一.多进程: 首先,先来讲一下fork之后,发生了什么事情. 由fork创建的新进程被称为子进程(child process).该函数被调用一次,但返回两次.两次返回的区别是子进程的返回值是0,而父进 ...

  8. Java 多线程(六)——进程间通信与线程间通信

    以前一直想找个机会总结一下进程和线程的通信机制,但由于技术和平台的局限性,一直没有找准切入点.由于马上要毕业了,对自己技术的总结和梳理的前提下写了本篇文章,如有错误之处,敬请拍砖和指教. 操作系统的主 ...

  9. linux 进程和线程或线程和线程之间通过管道通信(pipe)

    linux 进程和线程或线程和线程之间通过管道通信(pipe) 转自:http://blog.csdn.net/robertkun/article/details/8095331 线程间通信: [cp ...

最新文章

  1. vmstart的用法
  2. 【Windows】Windows Restart Manager 重启管理器
  3. Python函数默认参数陷阱
  4. ICML 2021 | AlphaNet:基于α-散度的超网络训练方法
  5. python中的常量可以修改吗_深入理解Python变量与常量
  6. [W3C][CSS]选择器(Selectors Level 3)[未完成]
  7. php 判断 跳转url参数,怎么通过链接获取跳转后的url参数
  8. jtm 一键安装mysql_一键安装MySQL
  9. Linux 搭建 KMS 服务器
  10. java线程状态切换图
  11. 人工智能导论(3)——确定性推理(Certainty Reasoning)
  12. 10 06 01 繁杂
  13. 基于R语言的方差分析及多重比较
  14. 背景图片随页面滚动放大缩小
  15. DAZ3D导入模型设置材质
  16. 我眼中的大数据(一)
  17. 声音发生器、pwm、占空比
  18. alisql mysql5.7_AliSQL 5.6.32 vs MySQL 5.7.15抢鲜测试
  19. [转]注册给排水工程师考试科目及内容
  20. usb线序usb线的接法

热门文章

  1. 和平精英微信名片服务器繁忙,和平精英微信游戏名片是什么 微信游戏名片设置方法一览[多图]...
  2. java全栈系列之JavaSE-面向对象(异常详解)043
  3. ul,ol,dl区别
  4. #三分法判断单峰函数最值#附加例题LA 5009
  5. 【专题】三分法和牛顿迭代法总结
  6. Python之路点燃编程圈:源于不爽C语言,单枪匹马搞副业,如今吞噬世界(附链接)...
  7. matlab多元函数数值积分,(数值积分)多元函数的某一变量进行定积分,int积分不出...
  8. Linux文件导入gitee仓库中
  9. 关于seekbar的thumb被截取的问题
  10. 大气简洁手绘风商务计划书PPT模板