Linux系统应用编程 --- 线程原语

1. pthread_create

#include <pthread.h>

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


pthread_t *thread:传递一个pthread_t变量地址进来,用于保存新线程的tid(线程ID)

const pthread_attr_t *attr:线程属性设置,如使用默认属性,则传NULL

void *(*start_routine) (void *):函数指针,指向新线程应该加载执行的函数模块

void *arg:指定线程将要加载调用的那个函数的参数

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

在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。

2. pthread_self

#include <pthread.h>

pthread_t pthread_self(void);

获取调用线程tid,其作用对应进程中getpid()函数。

线程ID:pthread_t类型,其本质是在Linux下是无符号整数,其他系统中可能是结构体实现

线程ID是进程内部识别标志。(两个进程间,线程ID允许相同)

注意:不应该使用全局变量pthread_t tid,在子进程中通过pthread_create传出参数来获取线程ID,而应该使用pthread_self()

 

线程ID与线程号的区别:

Ps –eLf命令看到的LWP那一列是线程号,线程号是CPU分配时间轮片的依据

Pthread_self得到的是线程ID,线程ID用来在进程内部区分线程

 

程序1:循坏创建多个子线程

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

void *thread_func(void *arg)
{
	int i = (int )arg;
	sleep(i);
	printf("%dth thread: thread id = %lu, pid = %u\n", i+1, pthread_self(), getpid());
	
	return NULL;
}

int  main(void)
{
	pthread_t tid;
	int ret, i;
	
	for(i=0; i<5; i++){
		ret = pthread_create(&tid, NULL, thread_func, (void *)i);
		if(ret != 0){
			fprintf(stderr, "pthread_create error:%s\n", strerror(ret));
			exit(1);
		}
	}
	
	//sleep(i);
	
	//return 0;			//将当前进程退出
	pthread_exit(NULL);
	
}

执行结果如下:

线程默认共享数据段、代码段等地址空间,常用的是全局变量,而进程不共享全局变量,只能借助mmap.

 

程序2:验证线程之间共享全局数据

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

int var = 100;

void *tfn(void *arg)
{
	var = 200;
	printf("thread.\n");
	
	return NULL;
}

int  main(void)
{
	printf("At first var = %d.\n", var);
	
	pthread_t tid;
	pthread_create(&tid, NULL, tfn, NULL);
	sleep(1);
	
	printf("after pthread_create, var = %d.\n", var);
	
	return 0;			//将当前进程退出
	
}

执行结果如下:

主控线程先结束,其他子线程也不会在执行 。如果任意一个线程调用了exit或_exit,则整个进程的所有线程都终止。

3. pthread_exit

#include <pthread.h>

void pthread_exit(void *retval);

void *retval:线程退出时传递出的参数,可以是退出值或地址,如是地址时,不能是线程内部申请的局部地址。一般传NULL。

pthread_exit与exit的区别:

调用线程退出函数pthread_exit,只是推出当前线程

任何线程里使用exit都会导致进程退出,主控线程退出时不能return或exit,因为会影响其他线程未工作。

 

exit与_exit的区别:

exit关闭C标准文件流,并刷新文件缓冲区

_exit(Linux底层函数),导致进程退出,关闭未关闭的文件描述符

 

需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

 

4. pthread_join

阻塞等待线程退出,获取线程退出状态,其作用对应进程中的waitpid()函数

#include <pthread.h>

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

pthread_t thread:回收线程的tid

void **retval:接收退出线程传递出的返回值

返回值:成功返回0,失败返回错误号

回收一个线程,获得线程退出值,如果线程没有终止,阻塞等待线程退出

5. pthread_cancel

在进程内的某个线程可以取消另一个线程

#include <pthread.h>

int pthread_cancel(pthread_t thread);

 

来一段code熟悉下刚才的API

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

void *thr_fn1(void *arg)
{
	printf("thread 1 returing\n");
	return (void *) 1;
}

void *thr_fn2(void * arg)
{
	printf("thread 2 exiting\n");
	pthread_exit((void * )2);
}


void *thr_fn3(void * arg)
{
	while(1)
	{
		printf("thread3 writing\n");
		sleep(1);
	}
}

int main(void)
{
	pthread_t tid;
	void * tret;
	
	pthread_create(&tid, NULL, thr_fn1, NULL);
	pthread_join(tid, &tret);
	printf("thread 1 exit code %d\n", (int )tret);
	
	pthread_create(&tid, NULL, thr_fn2, NULL);
	pthread_join(tid, &tret);
	printf("thread 2 exit code %d\n", (int )tret);
	
	pthread_create(&tid, NULL, thr_fn3, NULL);
	sleep(3);
	pthread_cancel(tid);
	pthread_join(tid, &tret);
	
	return 0;
}

执行结果如下:

 

6. pthread_detach

#include <pthread.h>

int pthread_detach(pthread_t tid);

pthread_t tid:分离线程tid

返回值:成功返回0,失败返回错误号。

 

一般情况下,线程终止后,其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL。如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。

7. pthread_equal

比较两个线程是否相等

 

线程终止方式

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

1.从线程主函数return。这种方法对主控线程不适用,从main函数return相当于调用exit。

2.一个线程可以调用pthread_cancel终止同一进程中的另一个线程。

3.线程可以调用pthread_exit终止自己

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页