11、POSIX多线程技术

目录

  • 11、POSIX多线程技术
    • 1、线程
    • 2、pthread_create函数
    • 3、pthread_exit函数
    • 4、pthread_join函数
    • 5、一个简单的线程化程序
    • 6、pthread_detach回收资源

11、POSIX多线程技术

1、线程

什么是线程

  • 在一个程序里的多个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”
  • 一切进程至少都有一个执行线程

fork和创建新线程的区别

  • 当一个进程执行一个fork调用的时候,会创建出进程的一个新拷贝,新进程将拥有它自己的变量和它自己的PID。这个新进程的运行时间是独立的,它在执行时几乎完全独立于创建它的进程
  • 在进程里面创建一个新线程的时候,新的执行线程会拥有自己的堆栈(因此也就有自己的局部变量),但要与它的创建者共享全局变量、文件描述符、信号处理器和当前的子目录状态

线程的优点

  • 创建一个新线程的代价要比创建一个新进程小得多
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作至少在理论上要少很多
  • 线程占用的资源要比进程少很多

线程的缺点

  • 编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的
  • 调试一个多线程程序也比调试一个单线程程序困难得多

线程函数库

  • 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
  • 要使用这些函数库,要通过引入头文件
  • 链接这些线程函数库时要使用编译器命令的“-lpthread”选项

线程标识符

  • 像每个进程有一个进程ID一样,每个线程也有一个线程ID
  • 进程ID在整个系统中是唯一的,但线程不同,线程ID只在它所属的进程环境中有效
  • 线程ID用pthread_t数据类型来表示,实现的时候可以用一个结构来代表pthread_t数据类型,所以可以移植的操作系统不能把它作为整数处理

2、pthread_create函数

  • 作用:创建一个新的线程
int pthread_create(pthread_t *thread,
  			pthread_attr_t *attr,
  			void*(*start_routine)(void*),
  			void *arg);
  • 参数
    • thread:新线程创建成功后,保存新线程的标识符
    • attr:设置线程的属性,一般不需要什么特殊的属性,直接传 NULL即可
    • start_routine: 是个函数地址,线程启动后要执行的函数
    • arg:传给线程启动函数的参数

"void* (start_routine)(void) "表示需要我们传递的一个函数地址,该函数以一个指向void的指针为参数,返回的也是一个指向void的指针。
调用成功时返回值是“0”,如果失败则返回一个错误。

3、pthread_exit函数

  • 作用:结束调用了这个函数的线程
void pthread_exit (void *retbal);
  • 返回一个指向某个对象的指针。
  • 绝不要用它返回一个指向一个局部变量的指针
  • 线程在结束时必须调用pthread_exit函数,这与一个进程在结束时要调用exit函数是同样的道理

4、pthread_join函数

  • 作用:在线程结束后把它们归并到一起
int pthread_join(pthread_t th, void **thread_return);
  • th: 指定了将要等待的线程标识符
  • thread_return: 它指向另外一个指针,而后者指向线程的返回值
  • 成功时返回“0”,失败时返回一个错误代码
  • pthread_join相当于进程用来等待子进程的wait函数

创建一个线程默认的状态是joinable,如果一个可结合线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收,所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源。

5、一个简单的线程化程序

#include 
#include 
#include 
#include 
#include 

char message[] = "Hello World";

void *thread_function(void *arg){
	printf("thread_function is running.Argument was %s\n",(char*)arg);
	sleep(3);
	strcpy(message,"Bye");
	pthread_exit((void *)"thank you");
}

int main() {
	int res;
	pthread_t a_thread;
	res = pthread_create(&a_thread,NULL,thread_function,(void*)message);
	if(res != 0) {
		perror("Thread creation failed");
		exit(EXIT_FAILURE);
	}

	printf("Waiting for thread to finish..\n");	
    void *thread_result;
	res = pthread_join(a_thread,&thread_result);
	if(res != 0) {
		perror("Thread join failed");
		exit(EXIT_FAILURE);
	}

	printf("Thread joined, it returned %s\n",(char*)thread_result);
	printf("Message is now %s\n",message);
	exit(EXIT_SUCCESS);
    return 0;
}

6、pthread_detach回收资源

  • 在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached)。一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。

  • 默认情况下,线程被创建成可结合的。为了避免存储器泄漏,每个可结合线程都应该要么被显示地回收,即调用pthread_join;要么通过调用pthread_detach函数被分离。

  • 由于调用pthread_join后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此。

  • 这时可以在子线程中加入代码 pthread_detach(pthread_self())

  • 或者父线程调用pthread_detach(thread_id)(非阻塞,可立即返回)

  • 这将该子线程的状态设置为分离的(detached),如此一来,该线程运行结束后会自动释放所有资源。

pthread_detach示例

#include 
#include 
#include 

void *print_message(void *ptr)
{
    pthread_detach(pthread_self());//将状态改为unjoinable状态,确保资源的释放
    static int g;
    printf("%d\n",g++);
    pthread_exit(0);//pthread_exit时自动会被释放
}

int main(int argc,char *argv[])
{
	pthread_t thread_id;
	while(1){// 一个线程默认的状态是joinable
		pthread_create(&thread_id,NULL,print_message,NULL);
	}
	return 0;
}

你可能感兴趣的:(Linux网络编程)