Appearance
pthread.h
Linux 目前主要使用的线程接口库是 NTPL:
new posix thread library
库的文件为 pthread.h ,函数基本以 pthread_ 开头。
编译的时候需要加上链接参数 -lpthread。
这个库的函数在错误时不会重置errno ,需要手动赋值。
phtread_create()
线程创建
函数原型
c
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);参数
@thread :是我们自己声明的 pid 变量,创建成功的话,函数会将 pid 保存其中。
@attr:指定线程分离还是结合的属性,目前弃用,使用 NULL 填写。
@start_routine 回调函数,是线程执行的任务。
@arg 传递给线程的参数,没有就填写NULL。
返回
成功返回 0,错误返回错误码(不重置 errno)并不对 thread 参数修改。
示例
c
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void*
pthread_function(void* arg)
{
while (1) {
printf("thread is running...\n");
sleep(1);
}
}
void*
thread_exec(void* arg)
{
int data = *(int*)arg;
while (1) {
printf("child thread 2 is running %d ...\n", data);
sleep(1);
}
}
int
main(int argc, char const* argv[])
{
pthread_t pt1, pt2;
int ret;
int data = 100;
ret = pthread_create(&pt1, NULL, pthread_function, NULL);
if (ret != 0) {
errno = ret;
perror("pthread_create()");
return -1;
}
ret = pthread_create(&pt2, NULL, thread_exec, (void*)&data);
if (ret != 0) {
errno = ret;
perror("pthread_create()");
return -1;
}
while (1) {
printf("main thread is running...\n");
sleep(1);
}
return 0;
}[!tip] Tip
可以使用结构体传多个参数。
pthread_exit()
线程退出
函数原型
c
#include <pthread.h>
void pthread_exit(void *retval);参数
@retval 线程结束的时候传出的地址,因为线程是通过回调函数的方式运行的,空间存在于栈中,所以该地址不应该是局部变量,应选择:
- 静态局部变量;
- 全局变量;
- 堆区变量。
返回
无返回值
关于主线程退出
- 主线程使用
return返回主函数或者exit关闭主线程,其所有子线程会立即终止; - 主线程使用
pthread_exit,进程不会退出,等所有子线程终止,主线程退出。
示例
看pthread_join部分的示例
pthread_join()
合并已经终止的线程。类似线程的 wait ,函数会阻塞。
函数原型
c
int pthread_join(pthread_t thread, void **retval);参数
@thread 线程的 tid
@retval 线程调用 pthread_exit 时候返回的值,不想接收,可以使用 NULL。
注意点
- 任何线程都可以等待其他线程退出
- 等待的线程没有结束则阻塞
- 线程结束,资源没有回收,可以用该函数释放
示例
c
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void *thread_exec(void *arg) {
// static int data = 100; // 返回静态变量
int *data = (int *)malloc(4); // 返回堆区变量
*data = 50;
// char *p = "hello"; // 返回只读区变量
while (1) {
printf("child thread is running ... \n");
sleep(1);
pthread_exit(data);
// pthread_exit(p);
}
}
int main(int argc, char const *argv[]) {
pthread_t tid;
int ret;
// char *retval;
int *retval;
ret = pthread_create(&tid, NULL, thread_exec, NULL);
if (ret != 0) {
errno = ret;
perror("Fail to pthread_create");
return -1;
}
while (1) {
printf("main thread is running ...\n");
pthread_join(tid, (void**)&retval);
printf("retval: %d\n", *retval);
// printf("retval: %s\n", retval);
sleep(5);
}
return 0;
}pthread_detach()
线程分为结合式和分离式,通过 pthread_create创建的线程是结合式的,它的特点是,子线程退出的时候,需要主线程释放资源,而分离式的则会有系统自动释放。这个函数可以用于分离结合式的线程。
函数原型
c
int pthread_detach(pthread_t thread);示例
就不写了,主线程里创建子线程后调用 pthread_detach(tid),就不需要最后再用 join 回收了。
pthread_cancel()
取消一个线程,类似于发结束信号。
函数原型
c
int pthread_cancel(pthread_t thread);于 pthread_exit 的区别在于,这个函数可以通过别的线程结束指定特定线程,相当于自杀和他杀的区别。因为是强制结束,所以也没有返回值了。
线程间通讯
练习
在主函数中创建一个数组 int a[5] = {10,20,30,40,50}在子线程中将数组循环打印出来。
c
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
struct array
{
int a[5];
int len;
};
void*
thread_show(void* arg)
{
int i = 0;
struct array* test = (struct array*)arg;
printf("child thread is running ....\n");
for (i = 0; i < test->len; i++) {
printf("%d\t", test->a[i]);
}
printf("\n");
}
int
main(int argc, char const* argv[])
{
struct array test;
int i = 0;
pthread_t tid;
test.len = 5;
for (i = 0; i < test.len; i++) {
scanf("%d", &test.a[i]);
}
pthread_create(&tid, NULL, thread_show, (void*)&test);
while (1) {
printf("main thread is running ...\n");
sleep(1);
}
return 0;
}