linux c线程控制 ——创建线程和终止线程

线程,我自己看到的两篇讲解比较生动形象的易于理解的博文,如下。


linux操作系统是支持多线程的,它在一个进程内生成了许多个线程。一个进程可以拥有一至多个线程。多线程相对于多进程还是有不少优点的:

  • 在多进程的情况下,每个进程都有自己独立的地址空间,而在多线程的情况下,同一进程内的的线程共享进程的地址空间。因此,创建一个新的进程时就要耗费时间来为其分配系统资源,而创建一个新的线程花费的时间就很少。
  • 在系统调度方面,由于进程地址空间独立而线程共享地址空间,线程间的切换速度要远远快过进程间的切换速度。
  • 在通信机制方面,进程间的数据空间相互独立,彼此通信要以专门的通信方式进行,通信时必须经过操作系统。而同一进程内的多个线程共享数据空间,一个线程的数据可以直接提供给其他线程使用,而不必经过操作系统。所以,线程间的通信更加方便和省时。
  • 可以提高应用程序的响应速度。
  • 可以提高多处理器的效率。
  • 可以改善程序的结构。对于要处理多个命令的应用程序,可以对每个命令的处理设计成一个线程,从而避免设计成大程序时造成的程序结构复杂。

    虽然线程在进程内共享地址空间,打开的文件描述符等资源。但是线程也有其私有的数据信息。

  • 线程号:每个线程都有一个唯一的线程号一一对应。
  • 寄存器
  • 堆栈
  • 信号掩码
  • 优先级
  • 线程私有的存储空间
    注意:编写linux下的多线程应用程序,需要头文件pthread.h,编译时需要加上-lpthread

    1、创建线程:

    线程的创建通过函数pthread_creat来完成。
    (1) 该函数声明及头文件如下:

#include<pthread.h>
int pthread_create(pthread_t *thread,pthread_attr_t *attr,
(void*)(*start_routine)(void*),void *arg);

编译链接参数
-lpthread

(2)返回值
若线程创建成功,则返回0。若线程创建失败,则返回出错编号。
返回成功时,由thread指向的内存单元被设置为新创建线程的线程ID。attr参数用于指定各种不同的线程属性。新创建的线程从start_routine函数的地址开始运行,该函数只有一个万能指针参数arg,如果需要向start_routine函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。
- thread:该参数为指向线程标识符的指针,当线程创建成功时,用来返回创建的线程id
- attr:该参数用来指定线程属性,NULL表示使用默认属性。
- start_routine:该参数是一个函数指针,指向线程创建后要调用的函数。
- arg:该参数指向传递该线程函数的参数。

(3)第二个参数是一个指向pthread_attr_t结构体(线程属性结构)的指针,该结构体如下:

  typedef struct
  {
             int                     detachstate;  //   线程的分离状态
             int                     schedpolicy;   //线程调度策略
             struct sched_param      schedparam;   //线程的调度参数
             int                     inheritsched;  //  线程的继承性
             int                     scope;          //线程的作用域
             size_t                  guardsize; //线程栈末尾的警戒缓冲区大小
             int                     stackaddr_set;//堆栈地址集
             void *                  stackaddr;     // 线程栈的位置
             size_t                  stacksize;      // 线程栈的大小
 }pthread_attr_t;

(4)创建线程这里还会用到的几个系统函数:

pthread_t pthread_self(void)      //获取本线程的id
int pthread_equal(pthread_t thread1,pthread_t thread2)   //判断两个线程id是否指向同一个线程
int pthread_once(pthread_once_t * once_control,void(*init_routine)(void))  //用来保证init_routine线程函数在进程中仅执行一次

下面为一个创建线程的例子

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
int * thread(void * arg)
{
        pthread_t newthid;
        newthid = pthread_self();
        printf("this is a new thread,thread ID = %lu\n",newthid);
        return NULL;
}
int main(void)
{
        pthread_t thid;
        printf("main thread,ID is %lu\n",pthread_self());  //pthread_self()获取本线程id。
        if(pthread_create(&thid,NULL,(void *)thread,NULL)!=0) //创建线程
        {
                printf("thread creation failed\n");
                exit(1);
        }
        sleep(1);
        exit(0);
}

运行结果:
先打印出主线程的ID,然后打印出新创建的线程ID。

main thread,ID is 140169071576896
this is a new thread,thread ID = 140169062905600

有时我们想在多线程的情况下,让某些函数只执行一次,该怎么办?
这时就要使用函数pthread_once
示例:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
pthread_once_t once = PTHREAD_ONCE_INIT;

void run(void)
{
        printf("Founction run is running in thread %lu\n",pthread_self());
}

void * thread1(void *arg)
{
        pthread_t thid=pthread_self();
        printf("当前的线程ID 是 %lu\n",thid);
        pthread_once(&once,run);
        printf("thread1 结束\n");
}

void * thread2(void * arg)
{
        pthread_t thid=pthread_self();
        printf("当前的线程 ID 是 %lu\n",thid);
        pthread_once(&once,run);
        printf("thread2 结束\n");
}

int main()
{
        pthread_t thid1,thid2;
        //创建线程 
        pthread_create(&thid1,NULL,thread1,NULL);
        pthread_create(&thid2,NULL,thread2,NULL);
        sleep(3);
        printf("主线程退出!\n");
        exit(0);
}

运行结果:

当前的线程 ID 是 140685518632704
Founction run is running in thread 140685518632704
thread1 结束
当前的线程 ID 是 140685510240000
thread2 结束
主线程退出!

我们可以看到两个线程都调用了run函数,但函数run只在线程thread1中运行了一次。

在多线程编程环境下,尽管pthread_once()调用会出现在多个线程中,init_routine()函数进执行一次,究竟在哪个线程中执行是不定的,是由内核调度来决定的


2、线程终止

linux下有两种方式可以使线程终止,第一种通过return从线程函数返回
第二种是调用函数pthread_exit()使线程退出。
pthread_exit头文件及其原型:

#include<pthread.h>
void pthread_exit(void * retval)

这里要注意两种特殊情况:

  • 在主线程中,如果从main函数返回或是调用了exit函数退出主线程,则整个进程将终止,此时进程中所有线程也将终止,因此在主线程中不能过早地从main函数返回。
  • 如果主线程调用pthread_exit函数,则仅仅是主线程消亡,进程不会结束,进程内的其他线程也不会终止,直到所有线程结束,进程才会结束。

(1)线程终止会带带来资源释放问题:特别是临界资源,临界资源在一段时间内只能被一个资源所持有,当线程要使用临界资源时需提出申请,如果该资源未被使用,则申请成功,否则等待。
临界资源被一个线程所独占,当一个线程终止时,如果不释放其占有的临界资源,则该资源会被认为还被已退出的线程所使用,因而永远不会得到释放。如果一个线程在等待使用这个临界资源,它就有可能无限的等下去,这就形成了死锁。再难也就因此而来。

为此,系统提供了一些函数,用于自动释放资源,从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(如调用pthread_exit)都将执行pthread_cleanu_push()所指定的清理函数

pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义:

#define pthread_cleanup_push(routine,arg)                                     
  { struct _pthread_cleanup_buffer _buffer;                                   
    _pthread_cleanup_push (&_buffer, (routine), (arg));
#define pthread_cleanup_pop(execute)                                          
    _pthread_cleanup_pop (&_buffer, (execute)); }

pthread_cleanup_push()带有一个"{",而pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译.


(2)线程终止还要注意线程间的同步问题,因为进程间各个现成的运行是相互独立的,线程的终止是不会相互通知的,也不会互相影响的,终止的线程所占用的资源并不会随着线程的终止而归还系统,而是仍归线程所在的进程持有,同样,对于这种情况,我们可以利用下面两个函数来解决问题。

#include<pthread.h>
void pthread_exit(void * retval);
int pathread_join(pthread_t th,void * thread_return);
int pthread_detach(pthread_t th);
  • pthread_join:以阻塞方式等待thred指定的线程结束。只有线程是可接入的时候,调用pthread_join才会成功,其中thrad:线程标识符,即线程ID,retval:用户定义的指针,用来存储被等待线程的返回值。
  • pthread_detach,这个函数可以改变线程的可接入属性,将其改变成分离模式。当线程处于分离模式,表明我们对于线程的返回值并不关心,工作线程执行完毕后会自行退出,内核会释放该线程占用的资源。
    一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join()的线程返回错误代码ESRCH.
    以下为主线程通过pthread_join等待铺主线程结束示例
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
void assisthread(void * arg)
{
        printf("I am helping to do some jobs\n");
        sleep(3);
        pthread_exit(0);
}
int main(void)
{
        pthread_t assistthid;
        int status;
        pthread_create(&assistthid,NULL,(void *)assisthread,NULL);//创建线程
        pthread_join(assistthid,(void *) &status);  //阻塞主线程。等待线程aassistthid结束
        printf("assistthread's exit is caused %d\n",status);

        return 0;
}

运行结果:

I am helping to do some jobs
assistthread's exit is caused 0

辅助线程线程的退出码为0,pthread_join得出的status也为0,一致。

©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页