Linux的内核中没有thread的概念,线程是第三方库libpthread实现的, 和vfork(轻量级进程,只有部分copy)有点像(进程的创建fork会完全copy主进程资源 ,而线程会共享资源,子线程创建新资源时其作用域只在当前子线程,而子线程非新新创建的资源会和创建前的主线程共享这些资源) , 线程和进程的创建在内核里都是系统调用copy_process ,但是他们的实现不同, 进程会调用fork()的copy_process(全拷贝) ,而线程调用的是thread的copy_process(第三方库实现的,只拷贝部分) , copy_process是一个虚函数
小知识: Linux 有个内存文件系统挂载在内存里(shell:cd /proc)用作调试使用, 里面有很多数,这些是进程或线程的ID, 这些文件里有他们的调试信息,比如 ps -ef 所展示的信息
shell:cd空格/work/pthread , 在这下面使用 pmap(进程映射工具)把进程信息保存(pmap [ThreadNum] > a.txt)
pmap -x [ThreadNum] >a.txt
pmap -x [ThreadNum] >b.txt
可以使用shell:diff a.txt b.txt 比对保存的两个进程或线程信息 有何区别
线程的概念 进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
(进程只是系统分配的资源而执行的是线程(在没有创建子线程时进程默认有一个线程执行,创建子线程后默认的线程作为主线程))
什么是堆 ,什么是栈 ,他们在哪 https://www.cnblogs.com/valor-xh/p/6535421.html
线程不代表资源,是程序的最小执行单元-----每一个线程都有一个栈 ,当线程结束时栈会被回收
进程代表资源,是程序调度的最小单元------创建的子线程共享主进程的一切资源, 而子进程新创建的资源不与主线程和其他线程共享
多线程是并发执行的,但也是有数量限制的,过了时间片要等待下一次的CPU调度 线程和进程除了资源的区别其他和进程一样
多线程的并行其实是伪并行,CPU的调度是有轮转的,比如A线程被CPU调度后,CPU寄存器等是被A所占用的,而A过了时间片后其状态和信息(寄存器里的值)会被保存在tss段, 而B线程会的状态和信息会被恢复到CPU
线程的管理
gcc -g -o xxx xxx.c -lpthread
pthread_self() 获得线程tid(由线程库维护的 ,其ID空间的各个进程独立的, ,因此每个进程的线程可能会相同)
syscall(SYS_gettid), 获取内核中的线程ID(内核中没有线程的概念只有轻量级进程 ,SYS_gettid则是获取内核中这个轻量级进程的ID 是唯一的)
syscall: http://blog.chinaunix.net/uid-28458801-id-4630215.html
线程的创建
int pthread_create(pthread_t *thread , const pthread_attr_t *attr , void(start_routine)(void*) , void *arg );
pthread_t *thread 线程的id 放一个指针,线程创建完后作为返回值返回给 传入的指针 返回指向线程的首地址
const pthread_attr_t *attr NULL 线程的属性
void(start_routine)(void*) 线程的执行函数
void *arg 传递给线程函数的参数
线程的退出与等待
void pthread_exit(void *retval); 自行退出当前线程 retval是一个返回值,可以NULL
其他线程调用 pthread_cancel
线程执行完后也是自动退出 , 创建线程的进程退出后线程也会退出
其中一个线程执行了exec类函数,因为会替换当前进程的所有的地址空间 线程也会退出
int pthread_join(pthread_t thread, void **retval); 等待指定线程退出后,会收到指定线程退出时返回的值 ,也可以NULL不接收
#include<pthread.h> #include<stdio.h> #include<stdlib.h> #include<string.h> void *helloworld(char *argc); int main(int argc,int argv[]) { int error; int *temptr; pthread_t thread_id; pthread_create(&thread_id,NULL,(void *)*helloworld,"helloworld"); //创建子线程 printf("*p=%x,p=%x ",*helloworld,helloworld); if(error=pthread_join(thread_id,(void **)&temptr)) //等待子线程退出,并接受返回的堆指针 { perror("pthread_join"); exit(EXIT_FAILURE); } printf("temp=%x,*temp=%c ",temptr,*temptr); *temptr='d'; //使用子线程创建的堆 printf("%c ",*temptr); free(temptr); //释放 return 0; } void *helloworld(char *argc) { int *p; p=(int *)malloc(10*sizeof(int)); //子线程创建了一个堆 printf("the message is %s ",argc); printf("the child id is %u ",pthread_self()); memset(p,'c',10); printf("p=%x ",p); pthread_exit(p); //把堆的指针返回出去 //return 0; }
线程的取消
线程能否被取消要看两点:
线程是否具有可取消属性---默认可以被取消
线程如果设置为到可取消点才能被取消时,线程被取消是不会被立刻取消
int pthread_cancel(pthread_t thread);
线程的取消状态属性
int pthread_setcancelstate(int state, int *oldstate);
state : PTHREAD_CANCEL_ENABLE 可取消属性
PTHREAD_CANCEL_DISABLE---->不可取消
线程的取消类型属性 A线程收到了B线程的取消请求时请求时 ,要根据取消类型判断是立即取消飞行合适再取消(取决于系统)
int pthread_setcanceltype(int type, int *oldtype);
type: 立刻被取消 PTHREAD_CANCEL_ASYNCHRONOUS
只有到达一定取消点,才会取消 PTHREAD_CANCEL_DEFERRED
线程的私有数据
TSD私有数据,同名但是不同内存地址的私有数据结构, 比如fork()后子进程继承了父进程的一切包括全局变量的值,但是在子进程修改了这个全局变量却不影响父进程的全局变量的值, 父进程的这个全局变量也不影响子进程的值, 两者不在同一内存地址互不影响
https://blog.csdn.net/mengxingyuanlove/article/details/50802246
int pthread_key_create(pthread_key_t key, void (destructor)(void*)); 创建
int pthread_key_delete(pthread_key_t key); 删除
void *pthread_getspecific(pthread_key_t key); 获取
int pthread_setspecific(pthread_key_t key, const void *value); 设置
//this is the test code for pthread_key #include <stdio.h> #include <pthread.h> pthread_key_t key; //线程私有类型的变量 void echomsg(void *t) { printf("destructor excuted in thread %u,param=%u ",pthread_self(),((int *)t)); } void * child1(void *arg) { int i=10; int tid=pthread_self(); printf(" set key value %d in thread %u ",i,tid); pthread_setspecific(key,&i); //要使用特定的设置函数才能设置当前线程私有的变量 printf("thread one sleep 2 until thread two finish "); sleep(2); printf(" thread %u returns %d,add is %u ",tid,*((int *)pthread_getspecific(key)),(int *)pthread_getspecific(key)); } void * child2(void *arg) { int temp=20; int tid=pthread_self(); printf(" set key value %d in thread %u ",temp,tid); pthread_setspecific(key,&temp); sleep(1); printf("thread %u returns %d,add is %u ",tid,*((int *)pthread_getspecific(key)),(int *)pthread_getspecific(key)); } int main(void) { pthread_t tid1,tid2; pthread_key_create(&key,echomsg); //创建线程私有的变量,每个线程都可以继承这个变量,但是内存地址不同而互不影响 ,在线程结束后会调用echomsg() pthread_create(&tid1,NULL,(void *)child1,NULL); pthread_create(&tid2,NULL,(void *)child2,NULL); pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_key_delete(key); //删除线程私有变量 return 0; }
线程占有的都是不共享的,其中包括:栈、寄存器、状态、程序计数器
线程间共享的有:堆,全局变量,静态变量;
线程的互斥的方式与更精细的互斥: 互斥锁,条件变量 , 读写锁
互斥锁的通信机制
pthread_mutex_t 互斥锁对象
int pthread_mutex_destroy(pthread_mutex_t *mutex); 销毁对象
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict 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); 解锁
无论读写共享内存都要先上锁,读完写完都要解锁
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #include <string.h> void *thread_function(void *arg); pthread_mutex_t work_mutex; //互斥锁 #define WORK_SIZE 1024 char work_area[WORK_SIZE]; //共享内存区域 int time_to_exit = 0; //是否退出的信号 int main(int argc,char *argv[]) { int res; pthread_t a_thread; void *thread_result; res = pthread_mutex_init(&work_mutex, NULL); //初始化并创建锁对象 if (res != 0) { perror("Mutex initialization failed"); exit(EXIT_FAILURE); } res = pthread_create(&a_thread, NULL, thread_function, NULL); //创建线程 if (res != 0) { perror("Thread creation failed"); exit(EXIT_FAILURE); } pthread_mutex_lock(&work_mutex); //上锁 printf("Input some text. Enter 'end' to finish "); while(!time_to_exit) //判断是否输入了end,如果为1则退出循环写 { fgets(work_area, WORK_SIZE, stdin); //get a string from stdin //往共享区域里写 pthread_mutex_unlock(&work_mutex); //unlock the mutex //解锁 while(1) { pthread_mutex_lock(&work_mutex); //上锁 if (work_area[0] != '