并发编程之线程基本元素和状态的剖析
前言
在本篇文章当中讲主要给大家介绍并发编程当中关于线程的基础概念,并且深入剖析进程的相关属性和设置,以及线程在内存当中的布局形式,帮助大家深刻理解线程。
深入理解 基础例子介绍
在深入解析之前,我们先用一个简单的例子简单的认识一下 ,我们使用 创建一个线程并且打印 Hello world 字符串。
代码语言:javascript复制void func(void arg) {
printf("Hello World from tid = %ldn", pthread_self()); // pthread_self 返回当前调用这个函数的线程的线程 id
return NULL;
}
int main() {
pthread_t t; // 定义一个线程
pthread_create(&t, NULL, func, NULL); // 创建线程并且执行函数 func
// wait unit thread t finished
pthread_join(t, NULL); // 主线程等待线程 t 执行完成然后主线程才继续往下执行
printf("thread t has finishedn");
return 0;
}
编译上述程序:
代码语言:javascript复制clang helloworld.c -o helloworld.out -lpthread
或者
gcc helloworld.c -o helloworld.out -lpthread
在上面的代码当中主线程(可以认为是执行主函数的线程)首先定义一个线程pthread_create 线程属性,然后创建线程并且执行函数 func ,当创建完成之后,主线程使用 阻塞自己,直到等待线程 t 执行完成之后主线程才会继续往下执行。
我们现在仔细分析一下的函数签名,并且对他的参数进行详细分析:
代码语言:javascript复制int pthread_create(pthread_t thread, const pthread_attr_t attr,
void (start_routine) (void ), void arg);
深入理解参数 thread
在下面的例子当中我们将使用 得到线程的 id ,并且通过保存线程 id 的地址的变量 t 得到线程的 id ,对两个得到的结果进行比较。
代码语言:javascript复制void func(void arg) {
printf("线程自己打印线程tid = %ldn", pthread_self());
return NULL;
}
int main() {
pthread_t t;
pthread_create(&t, NULL, func, NULL);
printf("主线程打印线程 t 的线程 id = %ldn", (long)(&t));
pthread_join(t, NULL);
return 0;
}
上面程序的执行结果如下图所示:
根据上面程序打印的结果我们可以知道,变量 t保存的就是线程 id 的地址, 参数 t 和线程 id 之间的关系如下所示:
在上面的代码当中我们首先对 t 取地址,然后将其转化为一个 long 类型的指针,然后解引用就可以得到对应地址的值了,也就是线程的ID。
深入理解参数 arg
在下面的程序当中我们定义了一个结构体用于保存一些字符出的信息,然后创建一个这个结构体的对象,将这个对象的指针作为参数传递给线程要执行的函数,并且在线程内部打印字符串当中的内容。
代码语言:javascript复制typedef struct info {
char s[1024]; // 保存字符信息
int size; // 保存字符串的长度
}info_t;
static
void func(void arg) {
info_t in = (info_t)arg;
in->s[in->size] = '