Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

Gray-Ice

个人博客兼个人网站

本篇博文仅会列出本章节中所使用的函数。

select()

1
2
3
4
5
6
#include <sys/select.h>  // 头文件
int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict timeout); // 返回已准备好的描述符的非零的个数,若出错则为-1。
FD_ZERO(fd_set *set); // 清空fd_set
FD_CLR(int fd, fd_set *fdset); // 清空fdset中fd对应的位[博主注: 具体操作应该是置零]。
FD_SET(int fd, fd_set *fdset); // 在fdset中设置fd的位。[博主注: 可理解成将fd添加进fdset]
FD_ISSET(int fd, fd_set *fdset); // 查看fd是否在fdset中。如果fd目前在fdset中,则返回非零值,如果fd不再fdset中,就返回0。

当select触发后,对应的fd_set中的内容会被改变。

线程

创建线程

1
2
3
#include <pthread.h>  // 头文件
typedef void *(func)(void *);
int pthread_create(pthread_t *tid, pthread_attr_t *attr, func *f, void *arg); // 若成功则返回0,若出错则为非零。

pthread_create函数创建一个新的线程,并带着一个输入变量arg,在新线程的上下文中运行线程例程f。能用attr参数来改变新创建线程的默认属性。

获取线程ID

1
2
#include <pthread.h>  // 头文件
pthread_t pthread_self(void); // 返回调用者线程的ID。

终止线程

一个线程是通过下列方式之一来终止的:

  • 当顶层的线程例程返回时,线程会隐式地终止。
  • 通过调用pthread_exit函数,线程会显式地终止。如果主线程调用pthread_exit,它会等待所有其他对等线程终止,然后再终止主线程和整个进程。
1
2
#include <pthread.h>  // 头文件
void pthread_exit(void *thread_return); // 返回值为thread_return。
  • 某个对等线程调用Linux的eixt函数,该函数终止进程以及所有与该进程相关的线程。
  • 另一个对等线程通过以当前线程ID作为参数调用pthread_cancel函数来终止当前线程。
    1
    2
    #include <pthread.h>
    int pthread_cancel(pthread_t tid); // 若成功则返回0,若出错则为非零。

回收已终止线程的资源

线程通过调用pthread_join函数等待其他线程终止。

1
2
#include <pthread.h>
int pthread_join(pthread_t tid, void **thread_return); // 若成功则返回0,若出错则为非零。

pthread_join函数会阻塞,直到线程tid终止,将线程例程返回的通用(void*)指针赋值为thread_return指向的位置,然后回收已终止线程占用的所有内存资源。

分离线程

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

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

1
2
#include <pthread.h>  // 头文件
int pthread_detach(pthread_t tid); // 若成功则返回0,若出错则为非零。

pthtread_detach函数分离可结合线程tid。线程能够通过以pthread_self()为参数的pthread_detach调用来分离自己。[博主注: 其实意思就是你可以在线程中使用pthread_detach(pthread_self())来分离调用这段代码的线程]

初始化线程

1
2
3
#include <pthread.h>  // 头文件
pthread_once_t once_control = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *once_control, void(*init_routine)(void)); // 总是返回0

once_control变量是一个全局或者静态变量,总是被初始化为PTHREAD_ONCE_INIT。当你第一次用参数once_control调用pthread_once时,它调用init_routine,这是一个没有输入参数,也不返回什么的函数。接下来的以once_control为参数的pthread_once调用不做任何事情。[博主注: 其实它的功能就是调用一个无返回值且无参数的函数一次,以后再执行这个函数,如果还是同一个once_control参数,便不再调用了]

信号量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <semaphore.h>  // 头文件
int sem_init(sem_t *sem, int pshared, unsigned int value); // 初始化信号量
/*
sem_init的英文描述(来自man7.org):
sem_init() initializes the unnamed semaphore at the address pointed to by sem. The value argument specifies the initial value for the semaphore.

The pshared argument indicates whether this semaphore is to be shared between the threads of a process, or between processes.

If pshared has the value 0, then the semaphore is shared between the threads of a process, and should be located at some address that is visible to all threads (e.g., a global variable, or a variable allocated dynamically on the heap).

If pshared is nonzero, then the semaphore is shared between processes, and should be located in a region of shared memory (see shm_open(3), mmap(2), and shmget(2)). (Since a child created by fork(2) inherits its parent's memory mappings, it can also access the semaphore.) Any process that can access the shared memory region can operate on the semaphore using sem_post(3), sem_wait(3), and so on.

Initializing a semaphore that has already been initialized results in undefined behavior.

[博主的翻译(博主英语水平有限,有些地方翻译的比较生硬,若是有能力还请看英文版): sem_init()初始化sem指向的地址。
value参数指定信号量的初始值。pshared参数决定这个信号量是用于同一个进程下的多个线程 还是 用于多个进程。
如果pshared参数的值是0,那么信号量将会在一个进程下的多个线程之间共享。它的地址应该能够被所有线程所访问(如将其设置为全局变量或者动态分配的在堆上的内存)。
如果pshared的参数是非零值,那么信号量将会在多个进程之间共享。并且这个信号量应该位于共享内存中(请查看sem_open(),mmap(),和shmget())。当一个子进程被创建后,它会继承它父进程的内存映射,也能访问信号量。
任何进程都可以通过使用sem_post, sem_wait操作共享内存区。初始化一个已经初始化了的信号量是未定义行为。]
*/
int sem_wait(sem_t *s); // 如果s是非零的,那么该函数将s减一,并且立即返回。如果s为零,那么就挂起这个线程,直到s变为非零。而一个sem_post操作会重启这个线程。在重启之后,sem_wait操作将s减一,并将控制返回给调用者。
int sem_post(sem_t *s); // sem_post操作将s加一。如果有任何线程阻塞在sem_wait操作等待s变成非零,那么sem_post操作会重启这些线程中的一个,然后该线程将s减一,完成它的sem_wait操作。

评论



愿火焰指引你