Linux--线程控制

一、线程的概念

线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
在单个程序中同时运行多个线程完成不同的工作,称为多线程。

二、Linux中线程的特点

1、Linux中没有正真意义上的线程,是通过进程来模拟的,也就是说Linux的线程的本质就是进程。
2、管理线程的问题。每个进程拥有一个管理线程,进程内部线程的创建、销毁、同步都经过管理线程处理。通过管理线程,实现了下面对于多线程的要求:

系统必须有能力杀死整个进程。
线程栈的回收必须在对应线程结束后执行,所以线程不能自己处理。
将要终止的线程必须能够被等待,从而不至于成为僵尸。
线程数据区的回收需要对所有线程迭代执行,这个任务要管理线程完成。
如果主线程需要调用pthread_exit(),而进程并没有终止,主线程就睡眠,直到其他线程终止后,由管理线程唤醒主线程。
3、为了维护线程数据和内存,LinuxThreads使用进程地址空间的高位部分,即低于进程栈空间的部分。
4、基于信号机制实现线程同步。
5、LinuxThreads把每个线程实现为一个进程,拥有一个唯一的进程ID。
6、当进程收到终止信号,由管理线程负责用此信号杀死其他线程。
7、如果某个异步信号被发送,管理线程把信号传递给某个线程,如果该线程当前阻塞了该信号,则信号就是pending状态。
8、内核调度器实现对线程的调度。

三、线程与进程的区别

1、进程强调的是资源独占;而线程强调的是资源共享;
2、进程与进程之间是没有任何交际的;同一个进程下的线程与线程之间共享地址空间;
3、线程是在进程的地址空间内部运行的;
4、进程是承担资源分配的基本单位;线程则是实现资源调度的基本单位;
5、线程上下文切换比进程上下文切换要快得多。

四、线程操作函数

注意:因为Linux上的线程函数位于libpthread共享库中,因此在编译时要加上-l pthread。例如:

gcc -o test test.c -l pthread
1.创建线程

#include<pthread.h>
int pthread_create(pthread_t *thread,const pthread_attr_t* attr,void*(*start routine)(void*),void *arg)

返回值:成功返回0,失败时返回错误码。以前学的系统函数都是成功返回0,失败返回-1,而错误码保存在全局变量error中,pthread库的函数都是通过返回值返回错误码,虽然每个线程也都有一个error,但这是为了兼容其他函数接口而提供的,pthread库本身并不使用它,通过返回值返回错误码更加清晰。

参数:
thread:指向线程标识符的指针。
arr:用于设置线程的属性。
start routine:一个函数指针,新兴行间的线程从start routine函数的地址开始运行。
arg:一个无类型的指针,当我们需要为第三个参数函数指针传参时,那么便需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。可以理解为此arg参数就是上面函数指针的参数。
创建线程代码示例:

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

//新线程,每秒打印一次,打印五次
void* thread_run(void *arg)
{
    int count=0;
    while(count++<5)
    {
        sleep(1);
        printf("%s,  %d\n",(char*)arg,count);
    }
    printf("thread is over...\n");
    return NULL;
}

int main()
{
    //主线程
    printf("pthread\n");
    pthread_t tid;
    int ret=pthread_create(&tid,NULL,thread_run,"thread is running!!!!");
    if(ret != 0)
    {
        printf("pthread_creat error\n");
        return -1;
    }
    else
    {
        sleep(1);
        printf("pthread is running!!\n");
    }
    int exitCode;
    pthread_join(tid,(void**)&exitCode);
    printf("main is over..%d\n",exitCode);//新线程的退出码
    return 0;
}

这里写图片描述
上面代码中用到了一个pthread_join函数,此函数的功能就是等待一个线程的结束。

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

此例中让主线程等待新线程的结束。第一个参数为新线程的线程ID,第二个参数用来接收新线程的退出码。

2.线程的终止

线程终止有三种方式:
简单的从启动例程中返回,返回值是线程的退出吗码。上面的例子thread_run函数直接return就是这种方式。

线程可以被同一进程中的其他线程调用pthread_cancle终止。
例如在主线程中终止新线程,仍为上面的例子。pthread_cancle的参数为要结束线程的线程ID。
代码:

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

//新线程,每秒打印一次,打印五次
void* thread_run(void *arg)
{
    int count=0;
    while(count++<5)
    {
        sleep(1);
        printf("%s,  %d\n",(char*)arg,count);
    }
    printf("thread is over...\n");
    return NULL;
}

int main()
{
    //主线程
    printf("pthread\n");
    pthread_t tid;
    int ret=pthread_create(&tid,NULL,thread_run,"thread is running!!!!");
    if(ret != 0)
    {
        printf("pthread_creat error\n");
        return -1;
    }
    else
    {
        sleep(1);
        printf("pthread is running!!\n");
    }
    pthread_cancel(tid); //主线程中结束子线程
    int exitCode;
    pthread_join(tid,(void**)&exitCode);
    printf("main is over..%d\n",exitCode);//新线程退出码
    return 0;
}

这里写图片描述

执行结果应为主线程打印一次后直接结束,新线程退出码为-1。如果一个线程被其他线程调用pthread_cancel异常终止掉,它返回的退出码将是常数PTHREAD_CANCELED。这个宏被定义在pthread.h中,值为-1。

线程可以调用pthread_exit自己终止自己。
pthread_exit的参数为退出码。注意,pthread_exit的参数或者新线程return的指针所指向的内存单元必须是全局的或者由malloc分配的,因为当其他线程pthread_join得到这个返回指针时新线程函数已经退出了。
代码:

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

//新线程,每秒打印一次,打印五次
void* thread_run(void *arg)
{
    int count=0;
    while(count++<5)
    {
        sleep(1);
        printf("%s,  %d\n",(char*)arg,count);
    }
    printf("thread is over...\n");
    pthread_exit((void*)10);
}

int main()
{
    //主线程
    printf("pthread\n");
    pthread_t tid;
    int ret=pthread_create(&tid,NULL,thread_run,"thread is running!!!!");
    if(ret != 0)
    {
        printf("pthread_creat error\n");
        return -1;
    }
    else
    {
        sleep(1);
        printf("pthread is running!!\n");
    }

    int exitCode;
    pthread_join(tid,(void**)&exitCode);
    printf("main is over..%d\n",exitCode);//新线程的退出码
    return 0;
}

这里写图片描述
执行结果为:程序正常执行,但新线程退出码为10。

五、分离线程

一般情况下,线程终止后,资源不会被立即释放,其终止状态一直保留到其他线程调用pthread_join获取它的状态为止,这时系统擦才会释放它所占用的资源。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用额度所有资源,而不保留终止状态,这时主线程也不必一直阻塞等待,可以去做其他的工作。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL。对一个detach的线程调用pthread_join或者pthread_detach都可以把该线程置为detach状态,也就是说,不能对同一线程调用两次pthread_join,或者如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。

线程是可分离的(detached)或者结合的(joinable),一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,他的存储器资源(eg:栈)是不可释放的。相反一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。
默认情况下,线程被创建成可结合的。为了避免内存的泄露,每个可结合线程都应该被显示的回收,即调用pthread_join。若没有被join,则会造成资源,内存的泄露。

因为调用pthread_join后,如果应该被join的新线程没有运行结束,调用者会被阻塞,我们有时候并不希望如此。这时可在新线程中加入代码pthread_detach(pthread_self())或者主线程调用pthread_detach(thread_id)来将新线程设置成可分离的,如此一来,新线程运行结束后会自动释放所有资源。

代码:

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

//新线程,每秒打印一次,打印五次
void* thread_run(void *arg)
{

    pthread_detach(pthread_self());
    printf("%s\n",(char*)arg);
    return NULL;
}

int main()
{
    //主线程
    printf("pthread\n");
    pthread_t tid;
    int ret=pthread_create(&tid,NULL,thread_run,"thread is running!!");
    if(ret != 0)
    {
        printf("pthread_creat error\n");
        return -1;
    }

    //wait
    int ret1=0;
    sleep(1);
    if(0== pthread_join(tid,NULL))
    {
        printf("pthread wait success!\n");
        ret1 = 0;
    }
    else
    {
        printf("pthread wait failed!\n");
        ret1 = 1;
    }
    return ret1;
}

这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值