1.linux多线程概述

1.1概述

进程是系统程序执行和资源分配的基本单位。每个进程都有自己的数据段,代码段和堆栈段。
线程通常叫做轻量级进程,线程是进程的基本调度单元,每个进程至少有一个main线程,它与同进程中的其他线程共享进程空间(堆 代码 数据 文件描述符 信号等),只拥有自己的栈空间,大大减小了上下文切换的开销
线程与进程的使用优缺点:线程执行的开销小,占用的cpu少,线程之间的切换快,但不利于资源的管理和保护;进程相反,从可移植性来说多进程要好一些

1.2线程的分类

用户级线程核心级线程

1.3线程创建的Linux实现

Linux的线程是通过用户级的函数库实现的,一般采用pthread线程可实现线程的访问和控制。编译的时候后面加上-lpthread
                                   创建                   退出                      等待(父进程回收子进程)
多进程                       fork()                  exit()                     wait()
多线程                       pthread_creat   pthread_exit()     pthread_join()

2.进程的创建和退出

创建线程实际上就是确定调用该线程函数的入口点,线程的创建采用函数pthread_create。在线程创建以后,就开始运行相关的线程函数,在该函数运行完之后,线程就退出,这也是线程退出的一种方式。另一种线程退出的方式是使用函数pthread_exit()函数,这是线程主动退出行为。这里要注意的是,在使用线程函数时,不能随意使用exit退出函数进行出错处理,由于exit的作用是使调用进程终止,往往一个进程包括了多个线程,所以在线程中通常使用pthread_exit函数来代替进程中的退出函数exit。

由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放。正如进程之间可以通过wait()函数系统调用来同步终止并释放资源一样,线程之间也有类似的机制,那就是pthread_join函数。pthread_join函数可以用于将当前线程挂起,等待线程的结束。这个函数是一个线程阻塞函数,调用它的函数将一直等待直到被等待的线程结束为止,当函数返回时,被等待线程的资源被回收。

函数原型:

#include<pthread.h>

int pthread_create(pthread_t* thread,pthread_attr_t* attr,void*(*start_routine)(void*),void* arg);

void pthread_exit(void* retval);

通常形式为:

pthread_t pthid;

pthread_creat(&pthid,NULL,pthfunc,NULL)或者pthread_creat(&pthid,NULL,pthfunc,(void*)3);

pthread_exit(NULL)或者pthread_exit((void*)3) 3作为返回值被pthread_join函数捕获。

pthread_creat用来创建线程,成功返回0,失败 -1

参数thread(pthid)是传出参数,保存新线程的标识

参数attr是一个结构体指针,里面的元素分别指向新线程的运行属性

参数start_routine是一个函数指针没指向新线程的入口函数(函数名)

参数arg用于传递给第三个参数指向的入口函数,可以为NULL

例子1:

#include<stdio.h>
#include<pthread.h>
void* handler(void* arg){char* p=(char*) arg;printf("from main:%s\n",p);sleep(5);pthread_exit((void*)3);
}
int main(){pthread_t thd;char*p ="hello world";pthread_create(&thd,NULL,handler,(void*)p);printf("thd; %u\n",(unsigned)thd);int iret;printf("joining\n");pthread_join(thd,(void*)&iret);printf("iret:%d\n",iret);return 0;
}

编译的时候带上线程库的选项

3.线程的等待退出

3.1等待进程退出

线程从入口函数自然返回的,或者主动调用pthread_exit()函数,都可以让线程正常终止
函数的返回值可以被其他线程用pthread_join函数获得
pthread_join原型为
#include<pthread.h>
int pthread_join(pfread_t thread,void **thread_return);
1,该函数是阻塞函数,一直等到参数thread制定的线程返回,与多进程中的wait,waitpid类似,thread_return是传输参数,接受线程函数的返回值。如果线程通过调用pthread_exit()终止,则pthread_exit()中的参数相当与自然返回值,照样可以用pthread_join获取到
在上面例子1 有应用

3.2线程的取消

线程也可以被其他线程杀掉

4.线程的同步和互斥

4.1线程的互斥

有一套专门用于线程互斥的mutex函数。为什么要加索,因为多个线程共用进程的资源,要访问公共区间时(全局变量),当一个线程访问的时候,需要加上索防止其他线程对它进行访问
创建互锁有两种方法:静态方法动态方法
动态方法采用pthread_mutex_init()函数来初始化互斥索 原型如下:
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *mutexattr)
mutexattr用于制定互斥锁的属性(一个结构体),通常为NULL
建立一个锁的步骤:
1.定义一个全局的pthread_mutex_t lock;
2.在main中调用pthread_mutex_init函数进行初始化
3.在子进程函数中调用pthread_mutes_lock加锁
4.在子进程函数调用pthread_mutex_unlock解锁
5.最后在main用pthread_mutex_destroy函数进行销毁
例子2:
这个例子如果不加锁输出结果不到20000
#include<stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t mutex ;//传出参数,指向一个锁
int i = 0 ;
void* handler(void* arg)
{int index ;for(index = 0; index < 10000 ; index ++ ){pthread_mutex_lock(&mutex);//加锁i ++ ;pthread_mutex_unlock(&mutex);//解锁}
}
int main()
{pthread_t  thd1, thd2 ;char* p = "hello world" ;int a = 12345;int iret ;char* pret ;pthread_mutex_init(&mutex, NULL);//创建一个锁,传出这个锁的参数pthread_create(&thd1, NULL,handler,NULL);//创建一个线程,传出这个线程的参数thd1pthread_create(&thd2, NULL,handler,NULL);//printf("joining...\n");pthread_join(thd1, NULL);结束线程pthread_join(thd2, NULL);pthread_mutex_destroy(&mutex);销毁这个锁printf("i: %d \n", i);return 0 ;
}
售票的例子:./main 3 表示三个站台售票
/*************************************************************************> File Name: ticket1.c> Author: yang> Mail:826123027@qq.com > Created Time: 2014年08月26日 星期二 00:15:58************************************************************************/#include<stdio.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
typedef struct tag{int s_id;pthread_mutex_t *s_mutex;
}DATA,*pDATA;
int tacket_cnt=20;
void* handler(void *arg){pDATA p=(pDATA)arg;printf("%d on:\n",p->s_id);while(1){pthread_mutex_lock(p->s_mutex);if(tacket_cnt==0){printf("ticket out!\n");free(p);pthread_mutex_unlock(p->s_mutex);//如果缺少这个就是多层加锁,死循环了return (void*) 0;}tacket_cnt--;sleep(1);//防止都是一个站点售票,不给后面的机会,线程进来的都在这排队呢printf("%d server sell a tacket,all tackets:%d\n",p->s_id,tacket_cnt);pthread_mutex_unlock(p->s_mutex);sleep(1);//防止都是一个站点售票,不给后面的机会,因为一个while循环速度很快}
}
int main(int argc,char *argv[]){srand(getpid());int cnt = atoi(argv[1]);pthread_t *pthread=(pthread_t*)calloc(cnt,sizeof(pthread_t));//动态建立一个数组,就是线程的那个传出参数pthread_mutex_t mutex;pthread_mutex_init(&mutex,NULL);int index;for(index=0;index<cnt;index++){pDATA p=(pDATA)calloc(1,sizeof(DATA));//头是首地址p->s_id=index;p->s_mutex=&mutex;pthread_create(pthread+index,NULL,handler,(void*)p);//建立线程啊,如果想传出多个参数,就得建立结构体,p就是个结构体包括(锁和p_id售票站台号)}printf("joining.......\n");for(index=0;index<cnt;index++)pthread_join(*(pthread+index),NULL);//回收线程啦pthread_mutex_destroy(&mutex);//销毁锁
}

4.2线程的同步

1.条件变量

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待条件变量的条件成立而挂起,另一个线程使条件成立,为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起
1.创建和注销
条件变量和互斥锁一样,都有静态和动态的创建
例子:pthread_cond_init(&cond_pro,NULL);
cond_pro为输出参数
2等待和激发
pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex)pthread_cond_timedwait()计时等待
激发的条件有两种:pthread_cond_signal()激活一个等待改条件的线程,存在多个等待线程按如对顺训激活其中的一个,而pthread_cond_broadcast()激活所有等待线程
生产者与消费者
./main 5 6  一个是消费者的数量,一个生产者的数量
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<pthread.h>
#define CNT 20
typedef struct tag{int s_arr[CNT+1];int s_front;int s_tail;
}QUEUE,*pQUEUE;//定义了一个队列,任务队列
QUEUE my_que;
pthread_cond_t cond_pro,cond_con;
pthread_mutex_t mutex;
pthread_cond_t cond_pro,cond_con;
int que_empty(pQUEUE pq){return pq->s_front == pq ->s_tail;
}
int que_full(pQUEUE pq){return (pq->s_tail+1)%(CNT+1) == pq->s_front;
}
int que_cnt(pQUEUE pq){return (pq->s_tail - pq->s_front + CNT + 1)%(CNT + 1);
}
void *pro_handler(void* arg){pthread_detach(pthread_self());while(1){pthread_mutex_lock(&mutex);while(que_full(&my_que)){//当队列满的时候,所有生产这都得等着,特别注意这里的while,pthread_cond_wait是先解锁在抢锁,得保证出去这个循环的时候一定不是空pthread_cond_wait(&cond_pro,&mutex);}my_que.s_arr[my_que.s_tail]=rand()%100;my_que.s_tail=(my_que.s_tail+1)%(CNT+1);if(que_cnt(&my_que)==1){//从空变成有一个商品了,告诉所有消费者都过来消费pthread_cond_broadcast(&cond_con);}printf("produce a product,total:%d\n",que_cnt(&my_que));pthread_mutex_unlock(&mutex);sleep(rand()%3+1);}
}
void *con_handler(void* arg){pthread_detach(pthread_self());while(1){pthread_mutex_lock(&mutex);while(que_empty(&my_que)){//当队列空的时候,所有消费者都得等着pthread_cond_wait(&cond_con,&mutex);}my_que.s_front=(my_que.s_front+1)%(CNT+1);if(que_cnt(&my_que)==(CNT-1)){//当队列从满变成差一个满的时候,告诉所有生产者,可以生产了pthread_cond_broadcast(&cond_pro);}printf("consump a product ,total:%d\n",que_cnt(&my_que));pthread_mutex_unlock(&mutex);sleep(2);}
}
int main(int argc,char *argv[]){my_que.s_front = my_que.s_tail=0;int con_cnt,pro_cnt;pro_cnt = atoi(argv[1]);con_cnt = atoi(argv[2]);srand(getpid());pthread_mutex_init(&mutex,NULL);pthread_cond_init(&cond_pro,NULL);//等待条件初始化,cond为传出参数pthread_cond_init(&cond_con,NULL);pthread_t* arr=(pthread_t*)calloc(con_cnt+pro_cnt,sizeof(pthread_t));//定义线程描述符数组int index = 0;while(con_cnt>0){pthread_create(arr + index,NULL,con_handler,NULL);//建立线程index++;con_cnt--;}while(pro_cnt>0){pthread_create(arr + index,NULL,pro_handler,NULL);index ++;pro_cnt--;}printf("*****");while(1);pthread_mutex_destroy(&mutex);//结束锁pthread_cond_destroy(&cond_pro);//结束条件pthread_cond_destroy(&cond_con);return 0;
}

linux 多线程学习相关推荐

  1. linux多线程学习(七)——实现“生产者和消费者”

    在上一篇文章中,利用信号量实现了线程间的互斥,这一篇将要利用信号量的互斥同步机制来实现一个经典实例,就是"生产者和消费者". 1.简单描述生产者和消费者的问题. 有一个缓冲区和两个 ...

  2. linux多线程学习(六)——信号量实现同步

    在上一篇文章中已经用信号量来实现线程间的互斥,达到了互斥锁的效果,今天这篇文章将讲述怎样用信号量去实现同步. 信号量的互斥同步都是通过PV原语来操作的,我们可以通过注册两个信号量,让它们在互斥的问题上 ...

  3. linux多线程学习(二)——线程的创建和退出

    在上一篇文章中对线程进行了简单的概述,它在系统中和编程的应用中,扮演的角色是不言而喻的.学习它.掌握它.吃透它是作为一个程序员的必须作为.在接下来的讲述中,所有线程的操作都是用户级的操作.在LINUX ...

  4. linux多线程学习(五)——信号量线程控制

    在上一篇文章中,讲述了线程中互斥锁的使用,达到对共享资源互斥使用.除了使用互斥锁,信号量,也就是操作系统中所提到的PV原语,能达到互斥和同步的效果,这就是今天我们所要讲述的信号量线程控制. PV原语是 ...

  5. linux多线程学习(三)——线程属性设置

    在上一篇文章中,介绍了线程的创建和退出,以及相关函数的使用.其中pthread_create函数的第二个参数,是关于线程属性的设置,这也是今天所有讲述的.这些属性主要包括邦定属性.分离属性.堆栈地址. ...

  6. linux多线程学习(一)

    进程是系统中程序执行和资源分配的基本单位.每个进程都有自己的数据段,代码段和堆栈段,这就导致了进程在进行切换等操作起到了现场保护作用.但是为了进一步减少处理机的空转时间支持多处理器和减少上下文切换开销 ...

  7. Linux多线程学习 - sched_yield() CPU让权

    FROM: https://blog.csdn.net/xiaoyeyopulei/article/details/7965533 sched_yield()这个函数可以使 另一个级别等于或高于当前线 ...

  8. linux多线程学习设置线程调度权限

    pthread_setschedparam 设置线程的权限 int pthread_setschedparam(pthread_t target_thread, int policy, const s ...

  9. linux多线程学习(四)——互斥锁线程控制

    在前面的文章中提及到,一个进程中的多个线程是共享同一段资源的,由于线程对资源的竞争引出了锁.其中mutex是一种简单的加锁方法,这个互斥锁只有两种状态,那就是上锁和解锁,可以把互斥锁看作是某种意义上的 ...

  10. Linux多线程学习(七)sched_yield

    sched_yield()这个函数可以使用另一个级别等于或高于当前线程的线程先运行.如果没有符合条件的线程,那么这个函数将会立刻返回然后继续执行当前线程的程序. 在成功完成之后返回零,否则返回-1. ...

最新文章

  1. C语言--static全局使用示例
  2. FreeMarker对应各种数据结构解析
  3. bash: ifconfig: command not found
  4. zabbix告警升级的迷惑
  5. Swift3.0语言教程获取字符串长度
  6. XenApp增加输出画面的帧率,提高类视频应用体验
  7. python 判断子序列_LeetCode 392. 判断子序列 | Python
  8. how does local sandbox parse navigation target from hash
  9. oracle 磁盘挂载格式化,Linux简单磁盘挂载
  10. 我们究竟还要学习哪些Android知识?附赠课程+题库
  11. python二进制移位_python学习特辑——二进制和位运算篇
  12. 微型计算机c320r3怎么拆,联想C320一体机拆机图解
  13. flutter常用内置动画组件
  14. SqlCommandBuilder自动创建dataAdapter数据库操作命令
  15. 2022年CXO领导力峰会暨IT东方会技术高管年会
  16. win7无线局域网_局域网共享一键修复 19.3.13(推荐更新)
  17. 防止 跨站请求伪造(CSRF)
  18. java eml 附件_Javamail怎样写带附件eml文件?
  19. 计算机中求声音传输时间公式,计算机常用计算公式汇总
  20. 腾讯网上共享excel使用总结

热门文章

  1. Git 和 SVN之间的五个基本区别
  2. 【转】BLE开发的各种坑
  3. spring Bean的生命周期管理
  4. python——语音信号读取、分帧、加窗
  5. 【LeetCode】026. Remove Duplicates from Sorted Array
  6. activemq部署
  7. BZOJ 2882 后缀数组的傻逼实现
  8. html中出现的script失效
  9. Android中的Intent详细讲解【转】
  10. Vijos 1303