POSIX线程管理全解析
立即解锁
发布时间: 2025-08-22 01:29:15 阅读量: 1 订阅数: 7 


UNIX系统编程:通信、并发与线程精解
### POSIX线程管理全解析
#### 1. 线程管理概述
线程包通常包含线程创建、销毁、调度、互斥执行和条件等待等功能。典型的线程包还有一个运行时系统,能透明地管理线程,用户通常不会察觉到它的存在。当创建一个线程时,运行时系统会分配数据结构来保存线程的ID、栈和程序计数器的值,线程的内部数据结构可能还包含调度和使用信息。一个进程中的线程共享该进程的整个地址空间,它们可以修改全局变量、访问打开的文件描述符,并且能以各种方式相互协作或干扰。
POSIX线程有时被称为pthreads,因为所有线程函数都以pthread开头。以下是基本的POSIX线程管理函数总结:
| POSIX函数 | 描述 |
| --- | --- |
| pthread_cancel | 终止另一个线程 |
| pthread_create | 创建一个线程 |
| pthread_detach | 设置线程以释放资源 |
| pthread_equal | 测试两个线程ID是否相等 |
| pthread_exit | 退出一个线程而不退出进程 |
| pthread_kill | 向一个线程发送信号 |
| pthread_join | 等待一个线程 |
| pthread_self | 查找自己的线程ID |
大多数POSIX线程函数成功时返回0,失败时返回非零错误代码,并且它们不会设置errno,因此调用者不能使用perror来报告错误。如果处理了线程安全问题,程序可以使用strerror。POSIX标准明确规定,没有一个POSIX线程函数会返回EINTR,并且如果被信号中断,POSIX线程函数不需要重新启动。
#### 2. 通过ID引用线程
POSIX线程通过pthread_t类型的ID来引用。一个线程可以通过调用pthread_self来获取自己的ID。
```c
#include <pthread.h>
pthread_t pthread_self(void);
```
pthread_self函数返回调用线程的线程ID,该函数没有定义错误。
由于pthread_t可能是一个结构体,因此使用pthread_equal来比较线程ID是否相等。
```c
#include <pthread.h>
pthread_t pthread_equal(thread_t t1, thread_t t2);
```
如果t1等于t2,pthread_equal返回非零值;如果线程ID不相等,则返回0,该函数也没有定义错误。
以下是一个示例代码,当线程的ID等于mytid时,输出一条消息:
```c
pthread_t mytid;
if (pthread_equal(pthread_self(), mytid))
printf("My thread ID matches mytid\n");
```
#### 3. 创建线程
pthread_create函数用于创建一个线程。与一些线程设施(如Java编程语言提供的)不同,POSIX的pthread_create会自动使线程可运行,不需要单独的启动操作。
```c
#include <pthread.h>
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void *), void *restrict arg);
```
如果成功,pthread_create返回0;如果失败,则返回非零错误代码。以下是pthread_create的强制错误列表:
| 错误 | 原因 |
| --- | --- |
| EAGAIN | 系统没有足够的资源来创建线程,或者会超过进程中线程总数的系统限制 |
| EINVAL | attr参数无效 |
| EPERM | 调用者没有设置attr指定的调度策略或参数的适当权限 |
下面的代码段展示了如何在打开my.dat文件进行读取后创建一个线程来执行processfd函数:
```c
void *processfd(void *arg);
int error;
int fd;
pthread_t tid;
if ((fd = open("my.dat", O_RDONLY)) == -1)
perror("Failed to open my.dat");
else if (error = pthread_create(&tid, NULL, processfd, &fd))
fprintf(stderr, "Failed to create thread: %s\n", strerror(error));
else
printf("Thread created\n");
```
#### 4. 分离和连接线程
当一个线程退出时,除非它是分离的线程,否则不会释放其资源。pthread_detach函数用于设置线程的内部选项,指定线程退出时可以回收其存储空间。分离的线程退出时不会报告其状态,而非分离的线程是可连接的,直到另一个线程为它们调用pthread_join或整个进程退出,才会释放所有资源。
pthread_join函数使调用者等待指定的线程退出,类似于进程级别的waitpid。为了防止内存泄漏,长时间运行的程序最终应该为每个线程调用pthread_detach或pthread_join。
pthread_detach函数的参数是要分离的线程的ID:
```c
#include <pthread.h>
int pthread_detach(pthread_t thread);
```
如果成功,pthread_detach返回0;如果失败,则返回非零错误代码。以下是pthread_detach的强制错误列表:
| 错误 | 原因 |
| --- | --- |
| EINVAL | 线程不是一个可连接的线程 |
| ESRCH | 没有ID为thread的线程 |
下面的代码段创建并分离一个线程来执行processfd函数:
```c
void *processfd(void *arg);
int error;
int fd;
pthread_t tid;
if (error = pthread_create(&tid, NULL, processfd, &fd))
fprintf(stderr, "Failed to create thread: %s\n", strerror(error));
else if (error = pthread_detach(tid))
fprintf(stderr, "Failed to detach thread: %s\n", strerror(error));
```
pthread_join函数的原型如下:
```c
#include <pthread.h>
int pthread_join(pthread_t thread, void **value_ptr);
```
如果成功,pthread_join返回0;如果失败,则返回非零错误代码。以下是pthread_join的强制错误列表:
| 错误 | 原因 |
| --- | --- |
| EINVAL | 线程不是一个可连接的线程 |
| ESRCH | 没有ID为thread的线程 |
下面的代码展示了如何检索终止线程传递给pthread_exit的值:
```c
int error;
int *exitcodep;
pthread_t tid;
if (error = pthread_join(tid, &exitcodep))
fprintf(stderr, "Failed to join thread: %s\n", strerror(error));
else
fprintf(stderr, "The exit code was %d\n", *exitcodep);
```
如果一个线程执行`pthread_join(pthread_self());`,假设该线程是可连接的(非分离的),这将导致死锁。一些实现会检测到死锁并强制pthread_join返回错误EDEADLK,但POSIX:THR扩展并不要求进行这种检测。
#### 5. 线程退出和取消
进程可以通过直接调用exit、从main函数返回或让其他进程线程调用exit来终止,在这些情况下,所有线程都会终止。如果主线程在创建其他线程后没有工作要做,它应该阻塞直到所有线程完成,或者调用pthread_exit(NULL)。
调用exit会导致整个进程终止,而调用pthread_exit只会导致调用线程终止。一个线程从其顶级返回时,会隐式地调用pthread_exit,返回值(一个指针)作为pthread_exit的参数。如果进程的最后一个线程调用pthread_exit,进程将以返回状态0退出。
```c
#include <pthread.h>
void pthread_exit(void *value_ptr);
```
POSIX没有为pthread_exit定义任何错误。
线程可以通过取消机制强制其他线程返回。一个线程调用pthread_cancel来请求取消另一个线程,目标线程的类型和可取消状态决定了结果。
```c
#include <pthread.h>
int pthread_cancel(pthread_t thread);
```
如果成功,pthread_cancel返回0;如果失败,则返回非零错误代码,该函数没有定义强制错误。
当一个线程收到取消请求时,其行为取决于它的状态和类型。如果线程处于PTHREAD_CANCEL_ENABLE状态,它会接收取消请求;如果处于PTHREAD_CANCEL_DISABLE状态,取消请求将被挂起。默认情况下,线程处于PTHREAD_CANCEL_ENABLE状态。
pthread_setcancelstate函数用于更改调用线程的可取消状态:
```c
#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);
```
如果成功,pthread_setcancelstate返回0;如果失败,则返回非零错误代码,该函数没有定义强制错误。
以下是一个修改后的processfd函数,在调用docommand之前显式禁用取消,以确保命令不会在执行过程中被取消:
```c
#include <pthread.h>
#include "restart.h"
#define BUFSIZE 1024
void docommand(char *cmd, int cmdsize);
void *processfdcancel(void *arg) { /* process commands with cancellation */
char buf[BUFSIZE];
int fd;
ssize_t nbytes;
int newstate, oldstate;
fd = *((int *)(arg));
for ( ; ; )
{
if ((nbytes = r_read(fd, buf, BUFSIZE)) <= 0)
break;
if (pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldst
```
0
0
复制全文
相关推荐










