linux 线程 1.1 — 线程开发 API

Linux上线程开发API概要

多线程开发在Linux平台上已经有成熟的 pthread 库支持 (第三方库)。其涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。其中:

  1. 线程操作又分线程的创建,退出,等待3种。
  2. 互斥锁则包括4种操作,分别是创建,销毁,加锁和解锁。
  3. 条件操作有5种操作:创建,销毁,触发,广播和等待。

其他的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本元素的基本操作封装出来。详细请见下表:

在这里插入图片描述

线程的创建等待及退出

1. 线程的创建:

#include <pthread.h>
int pthread_create(pthread_t * thread, const pthread_attr_t * attr, void *(*start_rtn)(void *), void * arg);
返回 :	若成功返回0,否则返回错误编号且*thread中的内容是未定义的。
参数 :	thread 		  为指向线程标识符的指针。
		attr 		  用来设置线程属性。默认属性为 NULL
		*(*start_rtn) 线程运行函数的起始地址。
		arg		      (*start_rtn) 函数的参数。

当成功时,thread 所指向的内存单元被设置为新线程的 id ,attr 参数用于定制各种不同的线程属性,可设为 NULL,以创建默认属性的线程。

新创建的线程从 start_rtn 函数地址开始运行,该函数只有一个无类型指针,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。

Linux下用C语言开发多线程程序,Linux系统下的多线程遵循POSIX线程接口,称为pthread。

关于 pthread_t
typedef unsigned long int pthread_t;
//come from /usr/include/bits/pthreadtypes.h
用途:pthread_t用于声明线程ID。

pthread并非Linux系统的默认库,而是POSIX线程库。在Linux中将其作为一个库来使用,因此加上 -lpthread(或-pthread)以显式链接该库。函数在执行错误时的错误信息将作为返回值返回,并不修改系统全局变量errno,也无法使用perror()打印错误信息。

2. 线程的退出

单个线程可以通过以下三种方式退出,在不终止整个进程的情况下停止它的控制流:
  1)线程只是从启动例程中返回,返回值是线程的退出码。
  2)线程可以被同一进程中的其他线程取消。
  3)线程调用pthread_exit:
  	#include <pthread.h>
		int pthread_exit(void *rval_ptr);   
		线程通过调用pthread_exit函数终止执行,就如同进程在结束时调用exit函数一样。
		这个函数的作用是,终止调用它的线程并返回一个指向某个对象的指针。

3. 线程的等待

	int	pthread_join(pthread_t thread,void **rval_ptr);
		pthread_join()函数,以阻塞的方式等待 thread 指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。
	参数 : thread: 线程标识符,即线程ID,标识唯一线程。
			retval: 用户定义的指针,用来存储被等待线程的返回值。	
	返回值: 0代表成功。 失败,返回的则是错误号。 					

一个线程不能被多个线程等待,也就是说对一个线程只能调用一次pthread_join,否则只有一个能正确返回,其他的将返回ESRCH 错误。

其实在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统调用copy了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy过程和fork不一样。 copy后的进程和原先的进程共享了所有的变量,运行环境。这样,原先进程中的变量变动在copy后的进程中便能体现出来。

线程的脱离,暂且不议

5. 线程ID获取和比较

ID的获取
#include <pthread.h>
	pthread_t pthread_self(void);
	返回:调用线程自身的ID
线程的比较,因为考虑到可移植性,我们不能把简单的线程ID当做整数来处理,
因为不同的系统对线程的ID定义有可能不同,所以使用了如下函数:
#include <pthread.h>
	int pthread_equal(pthread_t tid1, pthread_t tid2);		
	比较线程 tid1 和 tide 是否为同一线程。
	返回:若相等则返回非0值,否则返回 0

下面我们来写代码,验证

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

//       int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
//                          void *(*start_routine) (void *), void *arg);


void * funcl(void * arg){

        static int ret = 10;    //  static  静态全局变量
        static char * p = "t1 is a run out";
        printf("this is pthread t1, t1 的 ID 为 : %ld \n",pthread_self());
        //errno 是记录系统的最后一次错误代码。代码是一个int型的值,在errno.h中定
义。查看错误代码errno是调试程序的一个重要方法。当linux C api函数发生异常时,一般>会将errno变量(需include errno.h)赋一个整数值,不同的值表示不同的含义,可以通过查看
该值推测出错的原因。在实际编程中用这一招解决了不少原本看来莫名其妙的问题。
        printf("t1's 参数为 %d \n",*(int *)arg);

        printf("while 1 结束\n");

        pthread_exit((void *)p);
}
int main(){

        int ret;
        pthread_t t1;
        int * arg;
        *arg = 30;

        ret = pthread_create(&t1,NULL,funcl,(void *)arg);
        if(ret == 0 ){
                printf("pthread creat is success \n");
        }
        else {

                printf("pthread_creat is fail \n");
                printf("erron  =  %d \n",errno);
                perror("is:  ");
        }
  printf("this is pthread t0, t0 的 ID 为 : %ld \n",pthread_self());

        //      while(1);     //  当主线程一旦退出以后,那么地址空间就会释放,此
时子进程就没有了。              

        //      pthread_join(t1,NULL);       //  while(1); 有点low 啊,我们可以>使用 pthread_join( 
        // 我们先写一个 NULL 不回收                   //  以阻塞的方式等待 thread 指定的线程结束
        char * pret;

        pthread_join(t1,(void **)&pret);
        printf("main : t1 quit : %s\n",(char *)pret);

        return 0;
}

我们之前说,线程是共享地址的,现在我们来康康,线程如何共享地址。
简单的验证一下

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

//       int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
//                          void *(*start_routine) (void *), void *arg);

void * funcl2(void * arg){

        static int ret = 10;    //  static  静态全局变量
        static char * p = "t2 is a run out";
        printf("this is pthread t2, t2 的 ID 为 : %ld \n",pthread_self());
        //errno 是记录系统的最后一次错误代码。代码是一个int型的值,在errno.h中定
  
        printf("t2's 参数为 %d \n",*(int *)arg);

        pthread_exit((void *)p);
}
void * funcl1(void * arg){

        static int ret = 10;    //  static  静态全局变量
        static char * p = "t1 is a run out";
        printf("this is pthread t1, t1 的 ID 为 : %ld \n",pthread_self());
        //errno 是记录系统的最后一次错误代码。代码是一个int型的值,在errno.h中定
义。查看错误代码errno是调试程序的一个重要方法。当linux C api函数发生异常时,一般>会将errno变量(需include errno.h)赋一个整数值,不同的值表示不同的含义,可以通过查看
该值推测出错的原因。在实际编程中用这一招解决了不少原本看来莫名其妙的问题。
        printf("t1's 参数为 %d \n",*(int *)arg);

        printf("while 1 结束\n");

        pthread_exit((void *)p);
}
int main(){

        int ret1;
        int ret2;
        pthread_t t1;
        pthread_t t2;
        int * arg;
        *arg = 30;

        ret1 = pthread_create(&t1,NULL,funcl1,(void *)arg);
        if(ret1 == 0){
                printf("pthread creat is success \n");
        }
        else {

                printf("pthread_creat is fail \n");
                printf("erron  =  %d \n",errno);
                perror("is:  ");
        }
		ret2 = pthread_create(&t2,NULL,funcl2,(void *)arg);
         printf("this is pthread t0, t0 的 ID 为 : %ld \n",pthread_self());

        //      while(1);     //  当主线程一旦退出以后,那么地址空间就会释放,此
时子进程就没有了。              

        //      pth read_join(t1,NULL);       //  while(1); 有点low 啊,我们可以
使用 pthread_join( 
        // 我们先写一个 NULL 不回收                   //  以阻塞的方式等待 thread 指定的线程结束
        char * pret;
pthread_join(t1,(void **)&pret);
        printf("main : t1 quit : %s\n",(char *)pret);

        pthread_join(t2,(void **)&pret);
        printf("main : t2 quit : %s\n",(char *)pret);
        return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值