再论线程创建函数
int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*start_routine)(void*), void* arg);
详解线程属性 pthread_attr_t attr 属性
详解线程属性 API 函数
由于 pthread_attr_t 类型在每个系统中都有不同的定义, Linux 对外封装了 pthread_attr_t 类型,我们无法使用对结构体赋值的方式修改线程的属性,而是需要使用一系列设置线程属性的函数来修改线程属性
线程属性函数设置
线程属性实验
test1.c
#define _GNU_SOURCE /* To get pthread_getattr_np() declaration */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
static void print_attr(char* prefix, pthread_attr_t* attr)
{
int r = 0;
int i = 0;
size_t v = 0;
void* stkaddr = NULL;
struct sched_param sp = {0};
r = pthread_attr_getdetachstate(attr, &i);
printf("%s - Detach state(%d) = %s\n", prefix, r,
(i == PTHREAD_CREATE_DETACHED) ? "PTHREAD_CREATE_DETACHED" :
(i == PTHREAD_CREATE_JOINABLE) ? "PTHREAD_CREATE_JOINABLE" :
"???");
r = pthread_attr_getscope(attr, &i);
printf("%s - Scope(%d) = %s\n", prefix, r,
(i == PTHREAD_SCOPE_SYSTEM) ? "PTHREAD_SCOPE_SYSTEM" :
(i == PTHREAD_SCOPE_PROCESS) ? "PTHREAD_SCOPE_PROCESS" :
"???");
r = pthread_attr_getinheritsched(attr, &i);
printf("%s - Inherit scheduler(%d) = %s\n", prefix, r,
(i == PTHREAD_INHERIT_SCHED) ? "PTHREAD_INHERIT_SCHED" :
(i == PTHREAD_EXPLICIT_SCHED) ? "PTHREAD_EXPLICIT_SCHED" :
"???");
r = pthread_attr_getschedpolicy(attr, &i);
printf("%s - Scheduling policy(%d) = %s\n", prefix, r,
(i == SCHED_OTHER) ? "SCHED_OTHER" :
(i == SCHED_FIFO) ? "SCHED_FIFO" :
(i == SCHED_RR) ? "SCHED_RR" :
"???");
r = pthread_attr_getschedparam(attr, &sp);
printf("%s - Scheduling priority(%d) = %d\n", prefix, r, sp.sched_priority);
r = pthread_attr_getguardsize(attr, &v);
printf("%s - Guard size(%d) = 0x%lx bytes\n", prefix, r, v);
r = pthread_attr_getstack(attr, &stkaddr, &v);
printf("%s - Stack address(%d) = %p\n", prefix, r, stkaddr);
printf("%s - Stack size(%d) = 0x%lx bytes\n", prefix, r, v);
}
void* thread_entry(void* arg)
{
pthread_attr_t attr = {0};
printf("thread: %ld\n", pthread_self());
pthread_getattr_np(pthread_self(), &attr);
print_attr("thread", &attr);
return NULL;
}
int main()
{
pthread_t t = {0};
pthread_attr_t attr = {0};
int r = 0;
pthread_attr_init(&attr);
print_attr("init", &attr);
r = pthread_attr_setstacksize(&attr, 4 * 1024);
pthread_create(&t, &attr, thread_entry, NULL);
pthread_join(t, NULL);
pthread_getattr_np(pthread_self(), &attr);
print_attr("main", &attr);
pthread_attr_destroy(&attr);
return 0;
}
print_attr() 函数用于打印线程的属性
第 75 行,将子线程的栈大小设置为 4M
第 81 行,pthread_getattr_np() 函数用于获取线程的所有属性,需要调用这个函数的时候,需要在 #include <pthread.h> 前加上 #define _GNU_SOURCE
程序运行结果如下图所示:
子线程的栈大小成功的被改成了 4M,我们用 ulimit -s 命令查看默认线程栈大小为 8M
线程的结束方式
线程入口函数执行了 return 语句,并返回指定值
- return (void*)ret;
线程执行流调用 pthread_exit() 函数 (注意:void exit(int status);)
- void pthread_exit(void* ret);
其它线程对指定线程调用 pthread_cancel() 函数 (不安全)
- int pthread_cancel(pthread_t thread);
关于线程的返回值
基础类型的值
- 直接返回:pthread_exit(void* ret) or return (void*)ret;
结构体类型值
- 利用全局变量返回 or 通过 malloc() 利用堆内存返回
字符串返回值
- 利用静态字符串返回 or 字符串字面量返回
int pthread_join(pthread_t tid, void** retval);
该函数用于等待指定的线程 (tid) 执行结束
如果指定线程已经执行结束,则函数调用立即返回
如果指定线程必须不可连接,则函数调用失败
参数 retval 用于接收线程 返回值
线程的返回值实验
test2.c
#define _GNU_SOURCE /* To get pthread_getattr_np() declaration */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
void* thread_entry_1(void* arg)
{
char ret[] = "hello world";
printf("thread: %ld\n", pthread_self());
return ret; // OOPS!
}
void* thread_entry_2(void* arg)
{
double pi = 3.1415926;
void* ret = NULL;
memcpy(&ret, &pi, sizeof(ret));
return ret;
}
void* thread_entry_3(void* arg)
{
int* ret = malloc(4 * sizeof(int));
int i = 0;
for(i=0; i<4; i++) ret[i] = 4 - i;
return ret;
}
void* thread_entry_4(void* arg)
{
double* v = arg;
double r = *v;
*v = 3.1415 * r * r;
return NULL;
}
int main()
{
pthread_t t = 0;
double pi = 0;
void* ret = 0;
int* array = NULL;
printf("thread_entry_2:\n");
pthread_create(&t, NULL, thread_entry_2, NULL);
pthread_join(t, &ret);
memcpy(&pi, &ret, sizeof(pi));
printf("pi = %f\n", pi);
printf("thread_entry_3:\n");
pthread_create(&t, NULL, thread_entry_3, NULL);
pthread_join(t, (void**)&array);
for(int i = 0; i < 4; i++)
{
printf("array[%d] = %d\n", i, array[i]);
}
printf("thread_entry_4:\n");
pthread_create(&t, NULL, thread_entry_4, (void*)&pi);
pthread_join(t, NULL);
printf("pi = %f\n", pi);
return 0;
}
thread_entry_1() 返回了栈中的地址,函数返回后,所使用的栈就被销毁了,如果我们后续使用这个地址的话,那么行为是未定义的
thread_entry_2(),将 double 类型的变量 pi 拷贝给 void* 类型变量 ret,在64位系统忠,它们的大小都为 8 字节,所以这里的拷贝没有问题
thread_entry_3(),在堆空间上申请了一片内存,然后返回这块堆空间内存地址,这样是可行的
thread_entry_4(),通过修改线程入口参数地址上的值,来传递结果,这样也是可行的
程序运行结果如下图所示:
新解斐波那契数列
定义:
facc(n) = facc(n - 1) + facc(n - 2)
facc(1) = 1 : n => 1
facc(0) = 1 : n => 0
斐波那契数列多线程解法
facc2.c
#define _GNU_SOURCE /* To get pthread_getattr_np() declaration */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
typedef struct
{
int i;
int* arr;
} Array;
void* thread_facc(void* arg)
{
Array* a = arg;
int k = a->i;
if( a->arr[k] == 0 )
{
pthread_t t1 = {0};
pthread_t t2 = {0};
Array a1 = { k-1, a->arr };
Array a2 = { k-2, a->arr };
pthread_create(&t2, NULL, thread_facc, &a2);
usleep(k * 200);
pthread_create(&t1, NULL, thread_facc, &a1);
pthread_join(t2, NULL);
pthread_join(t1, NULL);
a->arr[k] = a->arr[k-1] + a->arr[k-2];
}
return NULL;
}
long long facc(unsigned int n)
{
int* arr = calloc(n, sizeof(int));
pthread_t t = {0};
long long ret = 0;
if( arr )
{
Array a = { n - 1, arr };
a.arr[0] = 1;
a.arr[1] = 1;
pthread_create(&t, NULL, thread_facc, &a);
pthread_join(t, NULL);
ret = a.arr[n-1] + a.arr[n-2];
}
free(arr);
return ret;
}
int main()
{
printf("ret = %lld\n", facc(20));
return 0;
}
Array 这个结构体用来记录第 n 项的斐波那契值,这样是为了避免重复计算已经计算出的斐波那契值
第 19 行,判断一下第 k 项的斐波那契值是否有计算过,如果没计算过,则创建线程计算
第 28 行,调用 usleep() 是为了避免某一时刻创建过多的线程,创建线程会消耗系统资源,如果某一时刻创建过多的线程,那么会导致栈资源被耗尽