1.线程的概念
main函数和信号处理函数是同⼀个进程地址空间中的多个控制流程,多线程也是如此,但是⽐信号处理函数更加灵活,信号处理函数的控制流程只是在信号递达时产⽣,在处理完信号之后就结束,⽽多线程的控制流程可以长期并存,操作系统会在各线程之间调度和切换,就像在多个进程之间调度和切换⼀样。
有些人把多线程和程序设计和多核系统联系起来,这种想法有些片面。即使程序在单核系统上,也可以做到多线程处理,并且使程序简化,所以处理器的数量并不会影响程序结构,而且,即使多线程程序在串行化任务时不得不阻塞。
同一进程的下的线程共享下面的资源
- 文件描述符
- 每种信号的处理方式(SIG_IGN,SIG_DFL或者自定义的信号处理函数)
- 当前工作目录
- 用户id和组id
但有些资源是每一个线程所独有的
1.线程id
2.上下文,包括各种寄存器的值,程序计数器和栈指针
3.栈空间
4.errno变量
5.信号屏蔽字
6调度优先级
特点提醒:在linux上线程序函数位于libpthread共享库中,因此在编译时要加上-lpthread选项
2.线程和进程的区别
a.地址空间和其它资源:进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
b.通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
c.调度和切换:线程上下文切换比进程上下文切换要快得多。
d.在多线程中,进程不是一个可执行的实体。
联系:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程全部资源。
3.线程控制的代码
(1)线程的创建
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
pthread_t ntid;
void printids(const char* s)
{
pid_t pid;
pthread_t tid;
pid=getpid();
tid=pthread_self();
printf("%s pid=%lu,tid=%lu\n",s,(unsigned long)pid,(unsigned long)tid);
}
void *thr_fn()
{
printids("new thread:");
return;
}
int main()
{
int id=pthread_create(&ntid,NULL,thr_fn,NULL);
if(id<0){
perror("pthread_create");
exit(1);
}
printids("main thread:");
sleep(1);
return 0;
}
int pthread_create(pthread_t *restict tidp,const pthread_attr_t *restrict attr,void* (start_rtn)(void*),void *restrict arg)
线程id是一个用pthread_t数据类型表示的,实现的时候要先定义一个全局的变量,不然打印将没有任何意义。
(2)线程终止
如果需要只终⽌某个线程⽽不终⽌整个进程,可以有三种⽅法:
1. 从线程函数return。这种⽅法对主线程不适⽤,从main函数return相当于调⽤exit。
2. ⼀个线程可以调⽤pthread_cancel终⽌同⼀进程中的另⼀个线程。
3. 线程可以调⽤pthread_exit终⽌⾃⼰。
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
void* thread1()
{
printf("thread1 returning....\n");
return (void*)1;
}
void* thread2()
{
printf("thread2 exiting...\n");
pthread_exit((void*)2);
}
void* thread3()
{
while(1){
printf("thread3 is running,wait for be cancal...\n");
sleep(1);
}
return NULL;
}
int main()
{
pthread_t id;
void* tret;
//从线程返回
pthread_create(&id,NULL,thread1,NULL);
pthread_join(id,&tret);
printf("thead return,thread id is:%u,return code is:%d\n",(unsigned int)id,(int)tret);
//线程自己调用pthread_exit()终止自己
pthread_create(&id,NULL,thread2,NULL);
pthread_join(id,&tret);
printf("thread exit,thread id is:%u,exit code is %d\n",(unsigned int)id,(int)tret);
//调用pthread_cancel()终止同一进程的另一个线程
pthread_create(&id,NULL,thread3,NULL);
sleep(3);
pthread_cancel(id);
pthread_join(id,&tret);
printf("thread3 return,thread id is %u,cancel code is %d\n",(unsigned int)id,(int)tret);
return 0;
}
void pthread_exit(void* retval);
注意:pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分 配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了
void pthread_cancel(pthread_t tid)
该函数来请求取消同一个进程中的其他线程。但是该函数并部等待线程终止,它仅仅提出请求
int pthread_join(pthread_t thread,void** retavl);
调用该含函数的线程将挂起等待,直到id为thread的线程终止。
4.线程的结合和分离
在任何⼀个时间点上,线程是可结合的(joinable)或者是分离(detached) 。 ⼀个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是不释放的。 相反, ⼀个分离的线程是不能被其他线程回收或杀死的,它的存储器 资源在它终⽌时由系统⾃动释放。要么被显⽰地回收,即调⽤pthread_join;要么通过调⽤pthread_detach函数被分离。
int pthread_detach(pthread_t tid)
这将该⼦线程的状态设置为分离的(detached),如此⼀来,该线程运⾏结束后会⾃动释
放所有资源.