linux——线程

来源:互联网 发布:linux mmap使用 编辑:程序博客网 时间:2024/06/18 13:49

线程的概念:
线程是进程内部的一条执行序列(执行流), 每个进程至少有一条执行序列:
main的执行体。

进程可以通过线程库创建N条线程, 这些新创建的线程称之为函数线程,main函数所代表的线程为主线程。

线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。在串行程序基础上引入线程和进程是为了提高程序的并发度,从而提高程序运行效率和响应时间。


线程与进程的区别:
1、 进程是资源分配的最小单位, 线程是CPU调度的最小单位
2、 线程是轻量级的进程
3、 管理方式不同, 进程是PCB管理, 线程是由线程结构管理。


线程和进程在使用上各有优缺点:

 线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。


用户级
内核级
混合模式


线程的创建:(线程库文件的使用)pthread
int pthread_create(pthread_t *id,   pthread_attr_t *attr,  void* (*fun)(void*),   void *arg);
创建一个函数线程!

与fork()调用创建一个进程的方法不同,pthread_create()创建的线程并不具备与主线程(即调用pthread_create()的线程)同样的执行序列,而是使其运行start_routine(arg)函数。thread返回创建的线程ID,而attr是创建线程时设置的线程属性(见下)。pthread_create()的返回值表示线程创建是否成功。尽管arg是void *类型的变量,但它同样可以作为任意类型的参数传给start_routine()函数;同时,start_routine()可以返回一个void *类型的返回值,而这个返回值也可以是其他类型,并由pthread_join()获取
包含线程创建函数的源代码要想生成可执行文件, 必须::
gcc -o pthread pthread.c-lpthread
创建出来的函数线程不同于函数调用, 函数调用是这条执行流中的一部分。

#include<stdio.h>#include<unistd.h>#include<string.h>#include<stdlib.h>#include<assert.h>//#include<fcntl.h>//#include<ctype.h>#include<pthread.h>#include<semaphore.h>void*pthread_fun(void*arg){char buff[128]="a b c d e f g h";char *q=NULL;char *p=strtok_r(buff," ",&q);while(p!=NULL){printf("fun::%s\n",p);sleep(1);p=strtok_r(NULL," ",&q);}}void main(){    pthread_t id;int res=pthread_create(&id,NULL,pthread_fun,NULL);    assert(res==0);int count=0;char buff[128]="0 1 2 3 4 5 6 7";char *q=NULL;char *p=strtok_r(buff," ",&q);while(p!=NULL){printf("main::%s\n",p);sleep(1);p=strtok_r(NULL," ",&q);}}

函数线程是创建出一条独立的执行序列, 他与主线程同时执行。
线程结束:
int pthread_exit(void *);参数可以设置线程结束状态
等待线程结束:
int pthread_join(pthread_t id, void **);
可以获取到等待的线程通过pthread_exit设置的结束状态信息
线程函数传参:
1、 将值强转成void*
int a = (int) arg;
2
、 将地址强转成void*
int a = *(int*)arg;

1、 线程同步
同步: 多进程或者多线程访问临界资源时, 必须进行同步控制。 多进程或者多线
程的执行并不完全是绝对的并行运行,有可能主线程需要等待函数线程的某些条件的发
生。
多线程之间有几个特殊的临界资源:
全局数据、 堆区数据、 文件描述符 多线程之间共用
线程间同步控制方式:
1.1信号量#include <semaphore.h>
获取:int sem_init(sem_t *sem, int shared , int value);
sem:
是一个sem_t类型指针, 指向信号量对象。
shared:是否能在多进程间共享,Linux不支持, 0
value:
信号量的初始值
返回值:
0成功-1失败
P操作:int sem_wait(sem_t *sem); //阻塞运行
V操作:int sem_post(sem_t *sem);
删除:int sem_destroy(sem_t *sem);
互斥锁
概念: 完全控制临界资源, 如果一个线程完成加锁操作, 则其他线程无论如何
都无法再完成加锁, 也就无法对临界资源进行访问。
初始化:
int pthread_mutex_init(pthread_mutex_t *mutex,
pthread_mutex_attr_t *attr);
加锁:int pthread_mutex_lock(pthread_mutex_t *mutex); //阻塞运行
int pthread_mutex_trylock(pthread_mutex_t *mutex); //非阻塞版本
解锁:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
释放:int pthread_mutex_destroy(pthread_mutex_t *mutex);
1.3
条件变量
2、 线程安全--->可重入函数
有些库函数会使用线程间共享的数据, 如果没有同步控制, 线程操作就是不安全的,
所以, 我们使用这样一些函数时, 就必须使用其安全的版本
---》 可重入函数



线程中 fork的使用, 锁的变化
在线程中调用
fork函数, 子进程只会启用调用fork函数的那条线程, 其他线程 不
会启用。
子进程会继承其父进程的锁以及锁的状态, 但是
父子进程用的不是同一把锁, 父进
程解锁并不会影响到子进程的锁, 所以
子进程有可能死锁!!!
pthread_atfork(void (*prepare)(void), void (*parent)(void ), void (*child)(void));
指定在fork调用之后, 创建子进程之前, 调用prepare函数, 获取所有的锁,
然后创建子进程, 子进程创建以后, 父进程环境中调用
parent解所有的锁, 子进
程环境中调用
child解所有的锁, 然后fork函数再返回。 这样保证了fork之后, 子进程
拿到的锁都是解锁状态, 避免死锁。

练习  :统计end前几个单词

#include<stdio.h>#include<unistd.h>#include<string.h>#include<stdlib.h>#include<assert.h>//#include<ctype.h>//#include<fcntl.h>#include<semaphore.h>#include<pthread.h>char buff[128]={0};sem_t sem;pthread_mutex_t mutex;void *pthread_fun(void*arg){   while(1)   {      if(strncmp(buff,"end",3)==0)  {           break;    }   }  printf("%d\n",strlen(buff)-1);  pthread_mutex_unlock(&mutex);  sleep(1);   }void main(){//sem_init(&sem,0,0);pthread_mutex_init(&mutex,NULL);    pthread_t id;int res = pthread_create(&id,NULL,pthread_fun,NULL);assert(res==0);while(1){pthread_mutex_lock(&mutex);    printf("please input:");    fflush(stdout);//char buff[128]={0};    fgets(buff,128,stdin);    pthread_mutex_unlock(&mutex);sleep(1);//sem_post(&sem);if(strncmp(buff,"end",3)==0){break;}}     pthread_join(id,NULL);}


原创粉丝点击