您的位置:首页 > 运维架构 > Linux

Linux线程的操作以及与进程的区别

2017-06-15 15:34 435 查看

线程的概念

  Linux中不仅有进程这一概念,还有线程这一概念。简单来说,线程是指:在一个进程中,不同的执行流。线程的大部分概念和进程相似,所以他们有很大的相同之处。多线程之间的切换和调度就好像进程间的切换和调度一样,他们都在进程的地址空间运行,所以多线程之间可共享一些资源:

  1、文件描述符表;

  2、每种信号的处理方式;

  3、当前工作目录;

  4、用户id和组id;

  但是线程之所以是线程,区别之处就在于下面这些多线程间不能共享的资源:

  1、线程id;

  2、上下文信息,包括各种寄存器,程序计数器和栈指针;

  3、栈空间;

  4、errno变量;

  5、信号屏蔽字;

  5、调度优先级

  这些之中我们主要理解栈空间和上下文,线程之间的栈空间不同的话,则他们必不会相互干扰,只在自己的栈帧里面运行。上下文信息,寄存器、程序计数器、栈指针这些东西都是标记一个“进程”被切换后,能正确回到自己原先的位置继续运行,标记该“进程”的计数或该“进程”中的指针位置。

  很大程度上,线程就像一个缩减版的进程,所以Linux中我们把线程也叫做:“轻量级进程(LWP)”。

  试想:我们在一个程序之中,需要执行两个不同的执行流之时,根据以前的方法,我们肯定会不假思索的fork一个子进程。很正确,但是一个进程就有自己PCB、页表、mm_struct…很多东西,这可能会给我们的操作系统或服务器带来很大的负担。但是,在合适的时候创建线程就可以避免这些问题,因为一个进程可以创建多个线程,而只需要一个PCB。

 

线程创建:

  线程的创建:

  

#include <pthread.h>

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


  首先,在使用pthread_create这个函数之前,我们必须要有一个线程id,直接使用 pthread_t定义即可。第二个参数表示线程的属性,目前我们不关心,设置为NULL即可;第三个参数很明显是个函数指针,表明创建的这个线程的执行分支,就是这个函数指针里面的内容;第四个参数依旧设置为NULL,使系统指定为我们生成。

  这个函数的返回值为“int”,但这个作用并不大,主要是它会产生两个执行流,一个为主执行流,另外一个为分支执行流。主执行流即为当前作用域中pthread_create下方的代码,分支执行流即为函数指针表示的那个函数。

  获得线程id:

  

#include <pthread.h>

pthread_t pthread_self(void);


  线程被创建出来,当然要在执行完毕之后对其进行回收,这就跟进程中子进程退出后父进程会对子进程进行回收一样。

  

#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);


  val为“**”,目的是为了拿回pthread_create中传参的函数指针的函数的返回值。

  因为函数指针的参数为void*,为一个一级指针就够,所以为了将其拿出来,用户使用或其他我还不知道的地方使用,就需要传一个二级指针进去,将其取出。

 线程的退出:

 可以使用pthread_exit()退出,注意不是exit,exit会直接退出进程。

 

#include <pthread.h>

int pthread_cancel(pthread_t thread);


  取消函数,可以自己取消自己,也可以别人取消。线程被取消时,会返回宏PTHREAD_CANCELD表示((void*)-1)。

  

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_t tid1,tid2;

void func()
{
printf("tid1-->hello,world~\n");
printf("tid1-->hello,world~\n");
printf("tid1-->hello,world~\n");
printf("tid1-->hello,world~\n");
printf("tid1-->hello,world~\n");
}

void *fun1(void *val)
{
func();
printf("tid1-->fun-->I am a thread,tid: %u\n",pthread_self());
//  pthread_exit((void*)1);
printf("tid1-->process pid: %d\n",getpid());
//  sleep(2);
}

void *fun2(void *val)
{
printf("tid2-->I am fun1\n");
return (void*)1;
}
int main()
{
void* tmp;
pthread_create(&tid1,NULL,fun1,NULL);
printf("tid1-->process, pid: %d\n",getpid());
sleep(1);
printf("main-->I am a thread,tid: %u\n",pthread_self());
if(0==pthread_join(tid1,&tmp))
{
printf("pthread_join success\n");
}
pthread_create(&tid2,NULL,fun2,NULL);
printf("tid2-->process, pid: %d\n",getpid());
sleep(1);
if(0==pthread_join(tid2,&tmp))
{
printf("pthread_join success\n");
}
printf("tmp:%d\n",tmp);
return 0;
}




  我们可以发现,线程间的运行貌似也遵守进程那样的调度原则来运行线程,所以这个运行的先后次序会发生改变。具体到哪一个线程什么时候运行,运行多长时间关系到系统分配的时间片的问题。

线程的分离与结合:

  线程有两个属性:结合与分离。一般默认情况下,线程的属性为结合属性,此属性下,当前线程乐意被其他线程杀死或回收,再被其他线程回收之前,它的资源时不释放的。相反,分离属性下,一个线程是不可以被杀死或回收的,它的存储器资源在线程终止时由操作系统回收。

  默认情况下的设置,是为了防止存储器泄漏。要保证,一个线程能被显式的回收,可以使用pthread_join函数回收,也可以分离此线程,使用pthread_detach。 如果⼀一个可结合线程运行结束后但没有被join,则它的状态类似于进程中的Zombie Process, 即还有⼀部分资源没有被回收,所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源。

  由于调⽤用pthread_join后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我 们并不希望如此。例如,在Web服务器中当主线程为每个新来的连接请求创建⼀一个⼦子线程进 行处理的时候,主线程并不希望因为调⽤用pthread_join而阻塞(因为还要继续处理之后到来 的连接请求),这时可以在子线程中加入代码 pthread_detach(pthread_self()) 或者⽗父线程调用 pthread_detach(thread_id)(非阻塞,可立即返回) 这将该子线程的状态设置为分离的(detached),如此⼀来,该线程运行结束后会自动释放所有资源。

  即线程的分离时可以自己分离自己,也可以由别人分离自己。

  分离线程的创建:

  

#include <stdio.h>
#include <pthread.h>

pthread_t tid;

void* fun(void* val)
{
//pthread_detach(pthread_self());
printf("I am a thread:%u\n",pthread_self());
printf("%s\n",(char*)val);
return (void*)1;
}

int main()
{
pthread_create(&tid,NULL,fun,"pthread runnning...");
pthread_detach(tid);
sleep(1);//沉睡一秒,保证系统回收动作完成
if(pthread_join(tid,NULL)==0)
{
printf("pthread_join success!\n");
}
else
{
printf("pthread_join failed!\n");
}
return 0;
}




如有错误,请指出!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: