天天看点

unix环境高级编程之线程篇(一)

这篇文章先讲一些线程标识和创建,会在下一篇文章中介绍线程同步。

本章介绍线程篇,第一篇先着重讲线程标识,创建,终止以及一些需要注意的东西,后续继续更新线程同步。

一、线程标识

就像每个进程拥有一个自己的进程ID一样,每个线程也拥有自己的一个线程ID。进程ID在整个系统中是唯一的,但是线程ID不同,它只在它所属的进程环境中有效。进程id有个数据类型pid_t来表示,他是个非负整数。线程id则用pthread_t数据类型来表示,但是这个数据类型在移植的时候不能

简单的当成整数来处理,因此需要使用函数来判定两个线程id是否相等;

#include <pthread.h>

int pthread_equal(pthread_t tid1,pthread_t tid2);//若相等则返回非0值,否则返回0

线程需要获取自己的id可使用pthread_self()函数获取

#include <pthread.h>

pthread_t pthread_self(void);

二、线程创建

新增线程的创建可以调用pthread_create函数创建;

#include <pthread.h>

int pthread_create(pthread_t *tid,const pthread_attr_t *attr,void *(*start)(void),void *arg);//成功返回0,失败返回错误编号

当pthread_create成功返回时,tid指向的内存单元被设置为新创建线程的线程id。

attr参数用于定制各种不同的线程属性,可设置为NULL,创建线程时会使用默认的属性。

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

线程创建后并不能保证哪个线程先运行,可能是新创建的线程,也可能是调用线程。新创建的线程可以访问进程的地址空间,并且继承调用线程

的浮点环境和信号屏蔽字,但是该线程的未决信号集被清除。

例子:

#include <pthread.h>
#include <stdio.h>
	
pthread_t ntid;
	
void printids(const char* src)
{
	pid_t pid;
	pthread_t tid;
		
	pid = getpid();
	tid = pthread_self();
		
	pthread("%s pid:%u, tid:%u (0x%x)\n",src,(unsigned int pid),(unsigned int)tid,(unsigned int)tid);
}
	
void *thr_fn(*arg)
{
	printids("new thread:");
	return (void*)0;
}
	
int main()
{
	int err;
	err = pthread_create(&ntid,NULL,thr_fn,NULL);
	if(0 != err)
		printf("create pthread err:%s\n",strerror(err));
	printids("main thread:");
	sleep(1);
	exit(0);
}
           

这个例子要说明两个问题:

1、要处理主线程和新线程之间的竞争,所以主线程要用sleep,否则,因主线程和新线程谁先运行的不确定性,新线程可能在运行之前

主线程已经退出了。导致新线程无法运行。

2、新线程获取自己的id要使用pthread_self()函数获取,不能从全局变量ntid得到,因为新线程可能在主线程之前运行,这样直接

获取的ntid是不正确的。

linux下线程id比较合理,但是进程id的获取却并不匹配,这和linux线程的实现有关,linux使用clone系统调用来实现pthread_create,

clone系统调用创建子进程,这个子进程可以共享父进程一定数量的执行环境,而且这个数量可配。

三、线程终止

进程的任意线程调用了exit,_Exit,_exit,那么整个进程就会终止。以此类似,如果信号的默认动作是终止进程,那么将该信号发

送到线程也会终止整个进程。

单个线程有三种方式退出,在不终止整个进程的情况下停止它的控制流

1、线程指示从启动历程中返回,返回值是线程的终止码

2、线程可以被统一进程中的其他线程终止取消

3、线程调用pthread_exit。

#include <pthread>

void pthread_exit(void *rval_ptr);

rval_ptr是无类型的,进程中的其他线程可以调用pthread_join函数访问获取到这个值

int pthread_join(pthread_t thread,void *rval_ptr);//成功返回0,出错返回错误编号

调用者将一直阻塞,直到指定线程调用pthread_exit,或返回,或被取消。如果线程被取消,rval_ptr值为PTHREAD_CANCELED,线程可以设置为分离状态,如果线程已经设置为分离状态,那么调用pthread_join就会失败,返回EINVAL.

注意pthread_exit调用的返回值所指向的内存在线程终止后要合法的,否则主线程去访问这个指针指向内存会出现非法访问的问题。

例子比较少,会在后续持续更新,希望继续关注!

继续阅读