多线程编程

目录

创建线程和结束线程

pthread_create

pthread_exit

pthread_join

pthread_cancel

线程属性

POSIX信号量

互斥锁

互斥锁基础API

互斥锁属性

条件变量

线程和信号


早期Linux不支持线程,直到1996年,Xavier Leroy等人才开发出第一个基本符合POSIX标准的线程库Linux Threads。但LinuxThreads效率低而且问题很多。自内核2.6开始,Linux才真正提供内核级的线程支持,并有两个组织致力于编写新的线程库:NGPT(Next Generation POSIX Threads)和NPTL(Native POSIX Thread Library)。不过前者在2003年就放弃了,因此新的线程库就称为NPTL。NPTL比LinuxThreads效率高,且更符合POSIX规范,所以它已经成为glibc的一部分。本书所有线程相关的例程使用的线程库都是NPTL。

本章要讨论的线程相关的内容都属于POSIX线程(简称pthread)标准,而不局限于NPTL实现,具体包括:

  • 创建线程和结束线程。
  • 读取和设置线程属性
  • POSIX线程同步方式:POSIX信号量、互斥锁和条件变量

最后我们将介绍在Linux环境下,库函数、进程、信号与多线程之间的相互影响。

创建线程和结束线程

Linux系统上,它们都定义在pthread.h头文件中。

pthread_create

创建一个线程的函数是pthread_create。其定义如下:

#include<pthread.h>

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

thread参数是新线程的标识符,后续pthread_*函数通过它来引用新进程。其类型pthread_t的定义如下:

#include<bits/pthreadtypes.h>
typedef unsigned long int pthread_t;

可见,pthread_t是一个整型类型。实际上,Linux上几乎所有的资源标识符都是一个整型数,比如socket、各种System V IPC标识符等。

attr参数用于设置新线程的属性。给他传递NULL表示使用默认线程属性。线程拥有众多属性,我们将在后面详细讨论。start_routine和arg参数分别指定新线程将运行的函数及其参数。

pthread_create成功时返回0,失败时返回错误码。一个用户可以打开的线程数量不能超过RLIMIT_NPROC软资源限制。此外,系统上所有用户创建的线程总数也不得超过/proc/sys/kernel/threads-max内核参数所定义的值。

pthread_exit

线程一旦被创建好,内核就可以调度内核线程序来执行start_routine函数指针所指向的函数了。线程在结束时最好调用如下函数以确保安全、干净地退出:

#include<pthread.h>

void pthread_exit(void* retval);

pthread_exit函数通过retval参数向线程的回收者传递其退出信息。他执行完之后不会返回到调用者,而且永远不会失败

pthread_join

一个进程中所有线程都可以调用pthread_join函数来回收其他线程(前提是目标线程是可回收的),即等待其他线程结束,这类似于回收进程的wait和waitpid系统调用。pthread_join的定义如下:

#include<pthread.h>

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

thread参数是目标线程的标识符,retval参数是目标线程返回的退出信息。该函数会一直阻塞,直到被回收的线程结束为止。该函数成功时返回0,失败时则返回错误码。可能的错误如表。

错误码 描述
EDEADLK 可能引起死锁。比如两个线程互相针对对方调用pthread_join,或者线程对自身调用pthread_join
EINVAL 目标线程是不可回收的,或者已经有其他线程在回收该目标线程
ESRCH 目标线程不存在

pthread_cancel

有时候我们希望异常终止一个线程,即取消线程,它是通过如下函数实现的:

#include<pthread.h>

int pthread_cancel(pthread_t thread);

thread参数是目标线程的标识符。该函数成功时返回0,失败则返回错误码。不过,接收到取消请求的目标线程可以决定是否允许被取消以及如何取消,这分别由如下两个函数完成:

#include<pthread.h>

int pthread_setcancelstate(int state , int *oldstate);

int pthread_setcanceltype(int type , int *oldtype);

这两个函数的第一个参数分别用于设置线程的取消状态(是否允许取消)和取消类型(如何取消),第二个参数则分别记录线程原来的状态和取消类型。state参数有两个可选值:

  • PTHREAD_CANCEL_ENABLE允许线程被取消。它是线程被创建时的默认取消状态。
  • PTHREAD_CANCEL_DISABLE,禁止线程被取消。这种情况下,如果一个线程收到取消请求,则它会将请求挂起,直到该线程允许被取消。
  • PTHREAD_CANCEL_DEFERRED,允许目标线程推迟行动,直到它调用了下面几个所谓取消点函数中的一个:pthread_join、pthread_testcancel、pthread_cond_wait、pthread_cond_timedwait、sem_wait、sigwait。根据POSIX标准,其他可能阻塞的系统调用,比如read、wait,也可以成为取消点。不过为了安全起见,我们最好在可能会被取消的代码中调用pthread_testcancel函数以设置取消点。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值