NB:标准C是没有线程支持的,要在C中使用多线程,需要使用POSIX接口。本文介绍POSIX 多线程的基本函数。

1. pthread_create

函数原型:

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

函数功能:创建新的线程。

参数说明:

  • 函数成功后,thread参数保存新线程的线程号。
  • 第二个参数用于设置新创建线程的一些属性。如果使用系统默认的值,则可设为NULL。
  • 第三个参数是一个函数指针,是新创建线程的入口地址。该函数只能有一个无类型指针参数arg,并且返回值为void *.
  • 第四个参数即是入口函数的参数。如果有多个参数,需要将多个参数放到一个结构中,然后把结构的地址传给入口函数。若函数没有参数,则该参数可设为NULL。

返回值:成功返回0;失败返回错误码,失败的时候thread的值时未定义的。

使用注意点:

  • 线程创建时并不能保证哪个线程会先运行:是新创建的线程还是调用线程(主进程)。
  • 新创建的线程可以访问调用线程的地址空间,并且继承调用线程的浮点环境和信号屏蔽字,但是该线程的未决信号集被清除。
  • 函数调用失败时会返回错误码,但并不会设置errno。

2. pthread_equal

函数原型

int pthread_equal(pthread_t t1, pthread_t t2);

函数功能:比较线程号是否相等。线程号使用pthread_t类型表示,一般都实现为无符号长整形(unsigned long int)。但是可移植的操作系统实现不能把它作为整数处理(即不能用==来比较两个线程号是否相等),所以专门实现了一个函数来比较线程号的值。

参数说明:两个线程的线程号。

返回值:t1和t2相等,返回非0;否则返回0.

3. pthread_self

函数原型:

pthread_t pthread_self(void);

函数功能:获取本线程线程号,gettid函数也可以实现该功能。

返回值:函数总是成功,返回调用者的线程号。

4. pthread_exit

函数原型:

void pthread_exit(void *retval);

函数功能:退出本线程。

参数说明:参数以为一个无类型指针,用于向其他线程(有的话)返回退出状态。如果不关心退出状态,可以将参数设为NULL。

返回值:函数总是成功,且不是返回给调用者(而是pthread_join,如果有的话)。

关于线程终止的注意点:如果进程中任一线程调用了exit、_Exit或者_exit,那么整个进程就会终止。与此类似,如果一个信号的默认动作是终止进程,那么,把该信号发送到线程会终止整个进程。单个线程可以通过下列三种方式退出(只退出线程,不终止进程):

  • 线程只是从启动例程中返回,返回值是线程的退出码。比如执行了return语句。
  • 线程可以被同一进程中的其他线程取消,见后面的pthread_cancel函数。
  • 线程调用pthread_exit函数。

5. pthread_cancel

函数原型:

int pthread_cancel(pthread_t thread);

函数功能:请求取消同一进程中的其他线程。默认情况下,pthread_cancel函数会使得由thread标识的线程的行为表现为如同调用了参数为PTHREAD——CANCELED的pthread_exit函数,但是,线程可以选择忽略取消方式或是控制取消方式。

返回值:成功返回0,失败返回非0。

NB:pthread_cancel函数并不等待线程终止,它仅仅提出请求。

6. pthread_cleanup_push和pthread_cleanup_pop

函数原型:

void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute);

函数功能:线程可以安排它退出时需要调用的函数,这样的函数称为线程清理程序(thread cleanup handler)。线程可以建立多个清理程序。处理程序记录在栈中,也就是说它们的执行顺序与它们的注册顺序相反。

当线程执行以下动作时调用清理函数,调用参数为arg,清理函数rtn的调用顺序由pthread_cleanup_push来安排:

  • 调用pthread_exit时;
  • 响应取消请求时;
  • 用非0 execute参数调用pthread_cleanup_pop时

如果execute设置为0,清理函数将不被调用。无论哪种情况,pthread_cleanup_pop都将删除上次pthread_cleanup_push调用建立的处理程序。

7. pthread_join

函数原型

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

函数功能:等待某一个线程退出。调用线程将一直阻塞,直到thread指定的线程调用pthread_exit、从启动例程返回或被取消。如果线程只是从它的启动例程返回,retval将包含返回码。如果线程被取消,则retval的值为PTHREAD_CANCELED。如果对线程的返回值不感兴趣,可以将retval置为NULL。这样将只等待指定线程终止。

调用pthread_join会自动把线程设置为分离状态,这样资源就可以恢复。如果线程已经处于分离状态,pthread_join调用就会失败,返回EINVAL。

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

8. pthread_detach

函数原型

int pthread_detach(pthread_t thread);

函数功能:使thread指定的线程处于分离状态。

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

NB:当一个线程处于非分离状态时,我们称它是joinable的,也就是说我们可以调用pthread_join函数来等待线程退出,并获得退出状态。当线程处于分离状态时,我们称它是 not joinable的,也就是说我们不能调用pthread_join函数来等待线程退出。处于分离状态的线程退出后,会马上自动将其资源释放给操作系统。

最后附上一个程序实例:

/*
 * common.h
 *
 *  Created on: 2015-6-16
 *      Author: allan
 */

#ifndef COMMON_H_
#define COMMON_H_

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


#endif /* COMMON_H_ */



/*
 * pthread.c
 *
 *  Created on: 2015-6-16
 *      Author: allan
 */

#include "common.h"

/* 线程1入口函数 */
void* thr_fn1(void *arg)
{
	pthread_t	tid1;
	tid1 = pthread_self();
	printf("thread 1 is running, tid = %ldn", tid1);
	return ((void*)0);
}

/* 线程2入口函数 */
void* thr_fn2(void *arg)
{
	pthread_t	tid2;
	tid2 = pthread_self();
	printf("thread 2 is running, tid = %ldn", tid2);

	sleep(3);
	//return ((void*)0);	// 正常线程返回
	//exit(0);				// 会导致整个进程退出
	pthread_exit((void*)2);	// 退出
}

/* 驱动例程 */
int pthread_driver(void)
{
	pthread_t	tid1, tid2;
	int	err, is_equal;

	/* 创建线程1 */
	err = pthread_create( &tid1, NULL, thr_fn1, NULL);
	if (err)
	{
		printf("pthread_create errorn");
		return EXIT_FAILURE;
	}

	/* 创建线程2 */
	err = pthread_create( &tid2, NULL, thr_fn2, NULL);
	if (err)
	{
		printf("pthread_create errorn");
		return EXIT_FAILURE;
	}

	/* 判断线程1和线程2线程号是否相等 */
	is_equal = pthread_equal( tid1, tid2 );
	printf("is_equal = %dntid1 = %ldttid2 = %ldn", is_equal, tid1, tid2);

	void *retval;
	/* 主线程等待线程2退出,并获取其退出码 */
	err = pthread_join(tid2, &retval);
	if (err)
	{
		printf("pthread_join errorn");
		return EXIT_FAILURE;
	}
	printf("thread 2 return value = %dn", (int)retval);

	/* 等待一段时间,防止主线程先于线程1退出(因为对线程2调用了pthread_join,所以主线程肯定不会先于线程2退出)*/
	sleep(10);
	return EXIT_SUCCESS;
}


/*
 * main.c
 *
 *  Created on: 2015-6-16
 *      Author: allan
 */

#include "common.h"

int main(int argc, char *argv[])
{
#if 0
	if (pthread_driver())
	{
		printf("pthread_driver failed.n");
		return EXIT_FAILURE;
	}
#endif
	if (pthread_exit_bug_driver())
	{
		printf("pthread_exit_bug_driver failed.n");
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}