Linux C 线程控制(一)

一、线程

什么是线程?

  • 线程是计算机中独立运行的最小单位,运行时占用很少的系统资源。由于每个线程占用的CPU时间是由系统分配的,因此可以把线程看作操作系统分配CPU时间的基本单位。线程是交替进行的。

线程的优点:

  • 在多进程情况下,每个进程都有自己独立的地址空间,而在多线程情况下,同一进程内在线程共享进程的地址空间。因此,创建一个新的进程时就要耗费时间来为其分配系统资源,而创建一个线程花费的时间要少的多。
  • 在系统调度方面,由于进程地址空间独立而线程共享地址空间,线程间的切换速度要远快过进程间的切换速度。
  • 在通信机制方面,进程间的数据空间相互独立,彼此通信要以专门的通信方式进行,通信时必须经过操作系统。而同一进程内的多个线程共享数据空间,一个线程的数据可以直接提供给其他线程使用,而不必经过操作系统。因此线程间的通信更加方便省时。
  • 线程可以提高应用程序的响应速度。在图像界面程序中,如果有一个非常耗时的操作,她会导致其他操作不能进行而等待这个操作,此时界面响应用户操作的速度会变得很慢。多线程环境下可以将这个非常耗时的操作由一个单独的线程来完成。这个线程在用完操作系统分配给他的时间片后,让出CPU,这样其他的操作便有机会执行了。
  • 可以提高多处理器的效率。现在许多计算机都是采用多核技术,在这种情况下,可以让多个线程在不同的处理器上同时运行,从而大大提高程序执行速度。因此,多线程更能发挥硬件的潜力。
  • 可以改善程序的结构。对于要处理多个命令的应用程序,可以将对每个命令的处理设计成一个线程,从而避免设计成大程序时造成的程序结构复杂。

二、创建线程

  •  线程创建函数pthread_create

#include <pthread.h>
int pthread_create(pthread_t *thread,pthread_attr_t *attr,
(void*)(*start_routine)(void*),void *arg);
  1. thread:该参数是一个指针,当线程创建成功时,用来返回创建的线程ID。
  2. attr:该参数用于指定线程的属性,NULL表示使用默认属性,稍后将介绍该数据结构。
  3. start_routine:该参数为一个函数指针,指向线程创建后要调用的函数。这个被线程调用的函数也称为线程函数。
  4. arg:该参数指向传递给线程函数的参数。
  • 线程创建成功时,pthread_create函数返回0,若不为0则说明创建线程失败。线程创建成功后,新创建的线程开始运行第三个参数所指向的函数,原来的线程继续运行。
  • pthread.h中其他的系统调用

                                    函                            数                             说                            明
pthread_t pthread_self() 获取本线程的线程ID
int pthread_equal(pthread_t thread1,pthread_t thread2) 判断两个线程ID是否指向同一线程

int pthread_once(pthread_once_t*once_control,void(*init_routine)())

用来保证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()
{
    pthread_t thid;

    printf("main thread,ID is %lu\n",pthread_self());
    if(pthread_create(&thid, NULL, (void*)thread, NULL) != 0){
        printf("thread creation failed\n");
        exit(1);
    }
    sleep(1);
    exit(0);
}

编译运行的结果:

main thread,ID is 140200697169728
this is a new thread,thread ID = 140200688666368

程序首先打印主线程的ID,然后打印新创建的线程的ID。

某些情况下函数执行次数要被限制为一次,这时要用pthread_once函数。下面的例子创建两个线程,分别通过pthread_once调用同一个函数,结果只执行了一次:

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

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

void * thread1(void*arg)
{
    pthread_t thid = pthread_self();
    printf("Current thread's ID is %lu\n",thid);
    pthread_once(&once,run);
    printf("thread1 ends\n");
}

void * thread2(void*arg)
{
    pthread_t thid = pthread_self();
    printf("Current thread's ID is %lu\n",thid);
    pthread_once(&once,run);
    printf("thread2 ends\n");
}

int main()
{
    pthread_t thid1,thid2;

    pthread_create(&thid1,NULL,thread1,NULL);
    pthread_create(&thid2,NULL,thread2,NULL);
    sleep(3);
    printf("main thread exit! \n");
    exit(0);
}

运行结果如下:

Current thread's ID is 140174946285312
Fuction run is running in thread 140174946285312
thread2 ends
Current thread's ID is 140174954678016
thread1 ends
main thread exit! 
  • 线程属性

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;


  • detachstate:表示新创建的线程是否与进程中其他的线程脱离同步。detachstate的缺省值为PTHREAD_CREATE_JOINABLE状态,这个属性也可以用函数pthread_detach()来设置。如果将detachstate设置为PTHREAD_CREATE_DETACH状态,则detachstate不能再恢复到PTHREAD_CREATE_JOINABLE状态。
  • schedpolicy:表示新线程的调度策略,主要包括SCHED_OTHER(正常、非实时)、SCHED_RR(实时、轮转法)和SCHED_FIFO(实时、先入先出)三种,缺省为SCHED_OTHER,后两种调度策略仅对root有效。
  • schedparam:一个struct sched_param结构,其中有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,缺省为0/
  • inheritsched:有两种值可供选择,PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程显示指定调度策略和调度参数(即attr中的值),而后者表示继承调用者线程的值。缺省为PTHREAD_EXPLICIT_SCHED。
  • scope:表示线程间竞争CPU的范围,也就是说,线程优先级的有效范围。POSIX的标准中定义了两个值,PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU,后者表示仅与同进程中的线程竞争CPU。
  • guardsize:警戒堆栈的大小。
  • stackaddr_set:堆栈地质集。
  • stacksize:堆栈的大小。

三、线程终止

  • 线程终止的两种方式

  1. 通过return从线程函数返回。

  2. 通过调用函数pthread_exit()使线程退出,该函数包含在pthread.h头文件中

#include <pthread.h>
void pthread_exit(void * retval);
  • 有两种特殊情况要注意:
  • 一种是,在主线程中,如果从main函数返回或是调用了exit函数退出主线程,则整个进程终止,此时进程中所有的线程也终止,因此在主线程中不能过早的从main函数返回;
  • 另一种是如果主线程调用pthread_exit函数,则仅仅是主线程消亡,进程不会结束,进程内的其他线程也不会终止,直到所有线程结束,进程才会结束。

线程终止最重要的问题是资源释放问题以,特别是一些临界资源。临界资源在一段时间内只能被一个线程所拥有,当线程要使用临界资源时需提出请求,如果该资源未被使用则申请成功,否则等待。临界资源使用完毕后要释放以便其他线程可以使用。

  • 为此Linux系统提供了两个函数自动释放资源

  • #include <pthread.h>
    #definr pthread_cleanup_push(routine,arg)       \
    { struct _pthread_cleanup_buffer;               \
             _pthread_cleanup_push(&buffer,(routine),(arg));
    #define pthread_cleanup_pop                     \
            _pthread_cleanup_pop(&buffer,(exeute));}

    从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作都将执行pthread_cleanup_push()所指定的清理函数。线程终止时另外一个要注意的问题是线程间的同步问题。一般情况下,进程中各个线程的运行是相互独立的,线程的终止并不会相互通知,也不会影响其他线程,终止的线程所占用的资源不会随着线程的终止而归还系统,而是仍为线程所在的进程拥有。正如进程之间可以使用wait()系统调用来等待其他进程结束一样,线程也有类似的函数:pthread_join()函数。

  • #include <pthread.h>
    void pthread_exit(void* retval);
    int pthread_join(pthread_t th,void* thread_return);
    int pthread_detach(pthread_t th);

    该函数用来等待一个线程的结束。他的调用者将被挂起并等待th线程终止,如果thread_return不为NULL,则*thread_return=retval。需要注意的是一个线程仅允许一个线程使用该函数等待他的终止,并且被等待的线程应该处于可join状态,即非DETACHED状态。DETACHED状态是指对某个线程执行pthread_detach()后其所处的状态。处于DETACHED状态的线程无法由pthread_join同步。一个可join的线程所占用的内存仅当有线程对其执行了pthread_join()后才会释放,因此为了避免内存泄漏,所有线程的终止时,要么已被设为DETACHED,要么使用pthread_join()来回收资源。

一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join()的线程返回错误代码ESRCH。

下面是一个线程终止的例子:

#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
void assisthread(void*arg)
{   
    printf("I am helping to do some jobs\n");
    sleep(3);
    pthread_exit(0);
}

int main()
{   
    pthread_t assistthid;
    int status;
    
    pthread_create(&assistthid,NULL,(void*)assisthread,NULL);
    pthread_join(assistthid,(void*)&status);
    printf("assistthread's exit is caused %d\n",status);
    
    return 0;
}

运行结果为:

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

从结果上看pthread_join会阻塞主线程,等待线程assisthread结束。pthread_exit结束时的退出码为0,pthread_join得出的status也为0.

猜你喜欢

转载自blog.csdn.net/weixin_42201172/article/details/81453567