linux系统下多线程的使用及常用线程函数的总结分析

0、前言

多线程的实质是并发,即同时执行多个任务,两个函数能同时执行,而不是像最初学的那样,main()函数里有多个函数,程序按函数先后顺序依次执行;

对于单核的cpu,并不是严格的并发,因为某一时刻只能执行一个任务,所以,此时的并发是指一个任务执行一点,然后又去执行另一个任务一点,任务之间进行不断的切换(总运行时间上并没有减少,反而增加了切换时间)。真正的并发是在多核的cpu上(同时运行多个任务,不需要切换,时间缩短)。

 Linux 操作系统下遵循POSIX thread接口,故为pthread 。

一个程序至少有一个线程,就是主函数本身。

以下均是linux下多线程的使用:

1、创建线程

#include <pthread.h>
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,(void*)(*start_rtn)(void*),void *arg);

tidp:指向线程标识符的指针;

attr:线程属性;

start_rtn:线程运行函数的起始地址,即函数指针,即函数名

arg:线程运行函数参数

返回值:若线程创建成功,则返回0。若线程创建失败,则返回出错编号。

多数用的时候,只有1,3参数,2,4参数置空。

例如:

#include <pthread.h>
void * myFunc(void* arg)
{
    printf("hello");//不用参数arg

    //int value = *((int*)arg)//用参数arg;
    //printf("%d\n",value);
};
void main()
{
    pthread_t myThread;
    int ret = pthread_create(&myThread,NULL,myFunc,NULL);
    if(ret!=0){printf("create thread fail");}
}

说明:

1参数为指针,在线程函数中对myTread初始化,返回线程id;

3参数为函数指针

4参数为3参数所指向的函数的参数,因为函数指针里有一个参数,故要传递该参数。

该参数实参在线程外初始化后,代入到线程中,最终代入到myFunc函数中,至于该参数如何处理,都是被调函数myFunc的操作。

2、退出线程

如同进程退出使用exit()类似,退出线程使用pthread_exit();

线程结束的三种方式:

1)调用pthread_exit函数

2)调用函数运行结束return.

3)线程被同一进程的其他线程取消

#include  <pthread.h>

void pthread_exit(void* ptr);

pthread_exit函数唯一的参数retval是函数的返回代码,只要pthread_join中的第二个参数retval不是NULL,这个值将被传递给ptr。

如果对返回值不感兴趣, 可以传递NULL.

线程返回值ptr,是 pthread_join(pthread_t thread, void **retval)中retval指向值;

ptr也是pthread_create(&myThread,NULL,myFunc,NULL)中void * myFunc(void* arg);的返回值void*

即:pthread_create()中的myFunc函数返回值即是pthread_exit()函数参数ptr,也是pthread_join()的函数参数retval指向值。

也就是说其他线程可以通过 pthread_jion 得到这个 无类型指针 ptr

例1:参数ptr的传递过程

分析:

1)void *ptr 是线程调用函数myFunc()的返回值;

因为是返回值,所以要求,ptr要么是动态分配的(存储在堆),要么在常量区,要么在全局/静态区,但不能是局部变量。

例如:动态分配的情况

void * myFunc(void *arg)
{
    int num = (int)arg;
    int *value = (int*)malloc(sizeof(int));//value是在栈区,指向的内存是在堆区
    if(num==1)
    {  
       *value = 100;
       pthread_exit((void*)value);//返回值是指向堆区的内存 //或return (void*)value;
    }      
    else
    {  
       *value = 200;
       return (void*)value;
    } 
        
}

例如:返回常量的情况

void * myFunc(void *arg)
{
    int num = (int)arg;
    if(num==1)
        pthread_exit("hello");//返回值是在常量区的字符串//或return "hello";
    else
        return("world");
}

2)此返回值返回在phread_join()函数中;

因为要将void*ptr 传递出去,所以pthread_join的形参需是void**   或者 void* &,函数定义采用了void**的用法。

pthread_jion(pthread_t thread, void **retval)
{
   //pthread_exit(void *ptr);//*ptr返回给retval
   *retval = ptr;
}

3)主线程调用phread_join函数获取到该ptr值,并修改ptr值//

int main()
{
    //do sth.  
    int *p;
    pthread_join(thread,(void**)&p);
    printf("p = %d\n",*p);
    free(p);
    p = NULL;
    //do sth.    
}

以上分析,完整示例如下:

void * myFunc(void *arg)
{
    int num = (int)arg;
    int *value = (int*)malloc(sizeof(int));//value是在栈区,指向的内存是在堆区
    if(num==1)
    {  
       *value = 100;
       pthread_exit((void*)value);//返回值是指向堆区的内存 //或return (void*)value;
    }      
    else
    {  
       *value = 200;
       return (void*)value;
    } 
        
}
int main()
{
    pthread_t mythread[2];
    int ret;

    int *retval;//join函数要返回的值

    for(int i =0;i<2;i++)
    {
        ret = phtread_create(&mythread[i],NULL,myFunc,(void*)i)
        if(ret!=0)
        {
             printf("thread%d create fail!\n",i+1);
             exit(1);
        }
   }
    for(int i =0;i<2;i++)
    {
        ret = phtread_join(mythread[i],NULL,(void**)&retval)
        if(ret!=0)
        {
            printf("thread%d join fail!\n",i+1);
        }
        printf("thread%d value = %d\n",i+1,*retval)
        free(retval);//释放内存
        retval = NULL;//置空
        
    }

}

注意:在主线程中使用pthread_exit();表示退出主线程,此时并不会退出子线程,这样也能保证子线程执行完毕,如下所示

void * myFunc(void *arg)
{
    int num = (int)arg;
   printf("%d\n",num);
        
}
int main()
{
    pthread_t mythread;
    int ret,i = 1;

       ret = phtread_create(&mythread,NULL,myFunc,(void*)i)
        if(ret!=0)
        {
             printf("thread%d create fail!\n",i);
             exit(1);
        }
   pthread_exit(NULL);//退出主线程

}

3、阻塞线程

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

thread:线程id

retval:被等待线程的返回值,多数情况下置NULL

以阻塞的方式等待thread指定的线程结束:在主线程pthread_join()执行处,等待子线程结束,并回收子线程资源,再执行接下来的程序;倘若pthread_join()执行时,子线程已经结束了,则直接回收子线程资源即可。

join的个人理解:本来在子线程创建后,主线程A和子线程B就开始并行了,A或B谁先执行完是不确定的,倘若子线程B先执行完,主线程A后执行完,这样不会出现问题;但倘若A先执行完,因为此时进程直接退出,若此时子线程B还未执行完,则出现错误。所以,为了避免后一种情况的发生,就引入了pthread_join(),在主线程未结束前将子线程加入到或插队到主线程中,就如同A和B都在各自的队伍里排队买东西,此时B插队到A的前面了,A就只能等B买完东西才能接着买了。

主线程A中有多个子线程时B,C时,每个子线程都要调用这个函数,即线程B,C,A进行排队执行。

例如:

void main()
{
    pthread_t myThread[2];
    pthread_create(&myThread[0],NULL,func,NULL);//myThread[0]线程从创建就开始执行
    pthread_create(&myThread[1],NULL,foo,NULL); //myThread[1]线程从创建就开始执行
    ......//主线程中的代码1

    pthread_join(myThread[0],NULL);//主线程中的代码2
                                   //代码1执行结束后,等待myThread[0]线程结束
                                   //若myThread[0]线程已经执行完,则直接回收资源
                                   //若myThread[0]线程未执行完,则等待线程结束后,回收资源

    pthread_join(myThread[1],NULL);//代码2执行结束后,此时myThread[0]线程已经结束
                                   //等待myThread[1]线程结束                        
                                   //若myThread[1]线程已经执行完,则直接回收资源
                                   //若myThread[1]线程未执行完,则等待线程结束后,回收资源
    ......//主线程中的代码3
    return;
}

注意:

pthread_join(myThread[0],NULL);此语句执行时,会一直等myThread[0]线程结束后才会继续执行下面的语句pthread_join(myThread[1],NULL);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值