Linux下线程编程

1.线程简介

  线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
  线程是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针PC,寄存器集合和堆栈组成。线程是进程的实体,是被系统独立调度和分配的基本单位一个线程可以创建和撤销另一个线程,同一进程的多个线程之间可以并发执行。线程由就绪、阻塞、运行三种基本状态。每一个程序至少有一个线程,若程序只有一个线程,那就是程序本身
  在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的地址空间(进程的地址空间),这意味着,线程可以访问该地址空间的每一个虚地址;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。

2.线程与进程区别

  进程是资源分配的基本单位。所有与该进程有关的资源,都被记录在进程控制块PCB中。以表示该进程拥有这些资源或正在使用它们。
  另外,进程也是抢占处理机的调度单位,它拥有一个完整的虚拟地址空间。当进程发生调度时,不同的进程拥有不同的虚拟地址空间,而同一进程内的不同线程共享同一地址空间。
  与进程相对应,线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。
  通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度,从而显著提高系统资源的利用率和吞吐量。因而近年来推出的通用操作系统都引入了线程,以便进一步提高系统的并发性,并把它视为现代操作系统的一个重要指标。
  线程与进程的区别可以归纳为以下4点:
  1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
  2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信需要进程同步和互斥手段的辅助,以保证数据的一致性。
  3)调度和切换:线程上下文切换比进程上下文切换要快得多。
  4)在多线程OS中,进程不是一个可执行的实体。
  进程和线程运行状态:

3.线程相关函数

 3.1创建线程pthread_create

  pthread_create是Unix操作系统(Unix、linux等)的创建线程的函数。
  注:编译时需要指定链接库 -lpthread,gcc -lpthread mythread.c -o mythread
  函数原型:

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
形参: thread — 指向线程标志符的指针类型为:pthread_t *
  attr — 设置线程属性,默认填NULL。类型为:const pthread_attr_t *
  void *(*start_routine) (void *) — 函数指针,现在运行函数的起始地址
  arg — 运行函数的参数。不需要填NULL ,类型为:void *
返回值: 成功返回0;失败返回错误编号。
  线程创建成功后,attr参数用于指定线程属性,新创建的线程函数形参只有一个void *形参,若需要传入的参数不止一个,则可以把需要传入的参数保存到一个结构体中,通过结构体传入。

示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *start_routine_func(void *arg)
{
    while (1)
    {
      printf("子线程运行中。。。\n");
      sleep(1);
    } 
}
int main()
{
    int stat;
    pthread_t pth;//线程标志符
    pthread_create(&pth,NULL,start_routine_func,NULL);
    while(1)
    {
        printf("主线程运行中。。。\n");
        sleep(1);
    }
}

运行结果:

[xsw@xsw 系统编程]$ gcc pthread.c -l pthread
[xsw@xsw 系统编程]$ ./a.out 
主线程运行中。。。
子线程运行中。。。
主线程运行中。。。
子线程运行中。。。
子线程运行中。。。
主线程运行中。。。

3.2 退出线程pthread_exit

  函数原型:

void pthread_exit(void *retval);
函数功能:
  终止调用它的线程并通过形参返回一个指向某个对象的指针
形 参: void *retval — 线程需要返回的地址
返回值: 无

  注:线程结束必须释放线程堆栈,也就是线程函数必须调用pthread_exit()结束,否则直到主进程函数退出才释放

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *start_routine_func(void *arg)
{
    int cnt=0;
    while (1)
    {
      printf("子线程运行中cnt=%d。。。\n",cnt);
      sleep(1);
      cnt++;
      if(cnt>=3)break;
    } 
    pthread_exit(NULL);//退出线程,释放堆栈
    
}
int main()
{
    int stat;
    pthread_t pth;//线程标志符
    /*创建子线线程*/
    if(pthread_create(&pth,NULL,start_routine_func,NULL)!=0)
    {
        printf("线程创建失败\n");
        return 0;
    }
    printf("子线程ID=%lu\n",pth);
    /*等待线程退出*/
    pthread_join(pth,NULL);
    printf("线程退出成功\r\n");
    return 0;
}

运行结果:

[xsw@xsw 系统编程]$ gcc pthread.c -lpthread
[xsw@xsw 系统编程]$ ./a.out 
子线程ID=3078433648
子线程运行中cnt=0。。。
子线程运行中cnt=1。。。
子线程运行中cnt=2。。。
线程退出成功

3.3 等待线程结束pthread_join

  函数原型:

int pthread_join(pthread_t thread, void **retval);
函数功能:
  以阻塞方式等待thread指定线程结束,当函数返回值,被等待线程的资源被回收。若线程已经结束,则立即返回。并且thread指定的线程必须是joinable(结合属性)属性。
形 参: thread — 线程标志符(线程ID)。线程唯一标志,类型为:pthread_t
  retval — 用户定义的指针,用来存储被等待线程返回的地址
返回值: 成功返回0,失败返回错误编号。

示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *start_routine_func(void *arg)
{
    int cnt=0;
    while (1)
    {
      printf("子线程运行中cnt=%d。。。\n",cnt);
      sleep(1);
      cnt++;
      if(cnt>=3)break;
    } 
    pthread_exit(NULL);//退出线程,释放堆栈
    
}
int main()
{
    int stat;
    pthread_t pth;//线程标志符
    /*创建子线线程*/
    if(pthread_create(&pth,NULL,start_routine_func,NULL)!=0)
    {
        printf("线程创建失败\n");
        return 0;
    }
    printf("子线程ID=%lu\n",pth);
    /*等待线程退出*/
    pthread_join(pth,NULL);
    printf("线程退出成功\r\n");
    return 0;
}

运行结果:

[xsw@xsw 系统编程]$ gcc pthread.c -lpthread
[xsw@xsw 系统编程]$ ./a.out 
子线程ID=3078433648
子线程运行中cnt=0。。。
子线程运行中cnt=1。。。
子线程运行中cnt=2。。。
线程退出成功

3.4 获取当前线程标志符pthread_self

  函数原型:

pthread_t pthread_self(void);
函数功能:
  获取线程自身ID。
形 参: 无
返回值: 返回当前线程标志符。pthread_t类型为unsigned long int,打印应%lu。

示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *start_routine_func(void *arg)
{
    printf("子线程ID=%lu运行中。。。\n",pthread_self());
    pthread_exit(NULL);//退出线程,释放堆栈
}
int main()
{
    int stat;
    int i=0;
    pthread_t pth;//线程标志符
    printf("主线程ID=%lu\n",pthread_self());
    /*创建5个子线线程*/
    for(i=0;i<5;i++)
    {
        if(pthread_create(&pth,NULL,start_routine_func,NULL)!=0)
        {
            printf("线程创建失败\n");
            return 0;
        }
        printf("子线程ID=%lu\n",pth);
    }
    /*等待线程退出*/
    pthread_join(pth,NULL);
    printf("线程退出成功\r\n");
    return 0;
}

运行结果:

[xsw@xsw 系统编程]$ gcc pthread.c -lpthread
[xsw@xsw 系统编程]$ ./a.out 
主线程ID=3078706880
子线程ID=3078703984
子线程ID=3068214128
子线程ID=3057724272
子线程ID=3047234416
子线程ID=3036744560
子线程ID=3068214128运行中。。。
子线程ID=3078703984运行中。。。
子线程ID=3057724272运行中。。。
子线程ID=3047234416运行中。。。
子线程ID=3036744560运行中。。。
线程退出成功

3.5 自动清理线程资源

  函数原型:

//注册清理函数
void pthread_cleanup_push(void (*routine)(void *),void *arg);
//释放清理函数
void pthread_cleanup_pop(int execute);
函数功能:
  线程清除处理函数,用于程序异常退出的时候做善后的资源清理。自动释放资源。
  注:pthread_cleanup_push函数与pthread_cleanup_pop函数需要成对调用。
形 参:
  void (*routine)(void *) — 处理程序函数入口
  void *arg — 传递给处理函数形参
  int execute — 执行的状态值,0 – 不调用清理函数;1 – 调用清理函数。
返回值: 无

导致调用清理函数条件:
  1.调用pthread_exit()函数
  2.Pthread_claenup_pop的形参为1
  注:return不会导致清理函数调用。

示例:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
/*线程清理函数*/
void routine_Clinen(void *arg)
{
    printf("arg=%d\n",*(int *)arg);
    free(arg);
    printf("释放空间完成\n");
}
/*子线程函数*/
void *start_routine_func (void *arg)
{
    printf("arg=%s,线程运行中...\n",arg);
    char *p=malloc(4);
    *p=100;
    //注册线程清理函数
    pthread_cleanup_push(routine_Clinen,p);
    pthread_exit("子线程返回数据测试!");//释放线程堆栈
   // return 0;//return终止不会触发线程清理函数
    //调用线程清理函数
    pthread_cleanup_pop(1);
}
int main() 
{
    /*1.创建线程*/
    char buff[]="线程传入参数测试";
    pthread_t thread;
    if(pthread_create(&thread,NULL,start_routine_func,buff)!=0)
    {
        printf("线程创建失败\n");
        return 0;
    }
    printf("线程ID=%lu\n",pthread_self());
    char *p;
    pthread_join(thread,(void **)&p);//等待线程退出
    printf("子线程返回数据:%s\n",p);
    printf("主线程退出\n");
    return 0;
}

运行结果:

[xsw@xsw 系统编程]$ gcc pthread.c -lpthread
[xsw@xsw 系统编程]$ ./a.out 
arg=线程传入参数测试,线程运行中...
线程ID=3078866624
arg=100
释放空间完成
子线程返回数据:子线程返回数据测试!
主线程退出

注:子线程退出时,return退出不会触发线程清理函数

3.6 线程取消函数pthread_cancel

int pthread_cancel(pthread_t thread);
函数功能:
  取消同一进程中的其他线程。
形 参:
  pthread_t thread — 线程描述符
返回值: 0 — 成功,其他值 — 失败

示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *start_routine_func(void *arg)
{
    int data=*(int *)arg;
    while(1)
    {
        printf("data=%d\n",data);
        sleep(1);
        data++;
    }
}
int main()
{
    int data=10;
    pthread_t pth_id;
    if(pthread_create(&pth_id,NULL,start_routine_func,&data)!=0)
    {
        printf("线程创建失败\n");
        return 0;
    }
    printf("子线程ID:%lu\n",pth_id);
    while(1)
    {
        sleep(1);
        printf("主线程运行中data=%d\n",data);
        data++;
        if(data==15)
        {
            pthread_cancel(pth_id);//取消子线程
        }
    }
}

运行结果:

[xsw@xsw 系统编程]$ gcc pthread.c -lpthread
[xsw@xsw 系统编程]$ ./a.out 
子线程ID:3079162736
data=10
主线程运行中data=10
data=11
主线程运行中data=11
data=12
主线程运行中data=12
data=13
主线程运行中data=13
data=14
主线程运行中data=14
主线程运行中data=15
主线程运行中data=16
主线程运行中data=17

3.7 线程分离属性pthread_detach

  创建一个线程默认的状态是joinable(结合属性),如果一个线程结束但没有调用pthread_join,则它的状态类似于进程中的zombie process(僵尸进程),即还有一部分资源没有被回收(退出状态码),所以创建线程时应该使用函数pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源(类似进程中的wait、waitpid)。但是调用pthread_join(pthread_id)函数后,如果该线程没有运行结束,调用者会被阻塞,有些情况下我们并不希望如此。pthread_detach函数可以将该线程状态设置为detached(分离状态),则该线程运行结束后自动会释放所有资源。

  函数原型:

int pthread_detach(pthread_t thread);
形 参:
  pthread_t thread — 线程标志符
返回值: 0 — 成功,其它值 – 失败

示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *start_routine_func(void *arg)
{
    int data=*(int *)arg;
    while(1)
    {
        printf("data=%d\n",data);
        sleep(1);
        data++;
    }
}
int main()
{
    int data=10;
    pthread_t pth_id;
    if(pthread_create(&pth_id,NULL,start_routine_func,&data)!=0)
    {
        printf("线程创建失败\n");
        return 0;
    }
    printf("子线程ID:%lu\n",pth_id);
    //设置分离属性
    pthread_detach(pth_id);
    //等待子线程退出
    pthread_join(pth_id,NULL);//未设置分离属性则会阻塞主线程
    while(1)
    {
        sleep(1);
        printf("主线程运行中...\n");
    }
    return 0;
}

运行结果:

[xsw@xsw 系统编程]$ gcc pthread.c -lpthread
[xsw@xsw 系统编程]$ ./a.out 
子线程ID:3078335344
data=10

主线程运行中...
data=11
主线程运行中...
data=12
主线程运行中...
data=13
主线程运行中...
data=14
主线程运行中...
data=15

3.8 设置线程栈空间

查看线程堆栈空间:

[wbyq@wbyq ~]$ ulimit -s
8192

8192单位是KB,也就是默认栈空间大小为8M
通过命令ulimit -a查看线程栈空间详细信息

[wbyq@wbyq ~]$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 15407
max locked memory       (kbytes, -l) 65536
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 15407
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

通过命令ulimit -s <栈空间大小>

[wbyq@wbyq ~]$ ulimit -s 10240
[wbyq@wbyq ~]$ ulimit -s 
10240

        每个线程的栈空间都是独立的,如果堆栈空间溢出程序会出现段错误。如果一个进程有10个线程,那么分配的栈空间大小为10*<每个线程栈空间大小>
示例:

#include <stdio.h>
int main()
{
    char buff[12*1024*1024+1]="hello,world\n";
    printf("buff=%s,%d",buff,sizeof(buff));
    return 0;
}

运行结果:

[xsw@xsw 系统编程]$ ./a.out 
段错误 (core dumped)

3.9 通过函数设置和查询线程栈空间

#include <stdio.h>
#include <pthread.h>
#include <limits.h>
int main()
{
    /*查看线程栈空间最小值*/
    printf("STACK_MIN:%d\n",PTHREAD_STACK_MIN);//16384byte--16kb
    pthread_attr_t attr;
    size_t ret,stack_size;
    ret=pthread_attr_init(&attr);//初始化线程属性
    if(ret!=0)
    {
        printf("初始化失败\n");
        return 0;
    }
    /*获取线程栈空间*/
    ret=pthread_attr_getstacksize(&attr,&stack_size);
    printf("线程栈空间:%ld kb\n",stack_size/1024);
    /*设置线程栈空间*/
    stack_size=8*1024*1024;//8M
    pthread_attr_setstacksize(&attr,stack_size);
    /*获取线程栈空间*/
    ret=pthread_attr_getstacksize(&attr,&stack_size);
    printf("修改后栈空间:%ld kb\n",stack_size/1024);
}
[wbyq@wbyq ubuntu]$ gcc main.c -pthread
[wbyq@wbyq ubuntu]$ ./a.out 
STACK_MIN:16384
线程栈空间:10240 kb
修改后栈空间:8192 kb



 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值