Linux多线程笔记一
基础知识
- 进程是资源管理的最小单位,线程是程序执行的最小单位。
- 每个进程有自己的数据段、代码段和堆栈段。
线程它包含独立的栈和CPU寄存器状态,线程是进程的一条执行路径。
每个线程共享其所附属进程的所有资源,包括打开的文件、内存页面信号标识及动态分配的内存等。 - 线程是属于进程的,线程运行在进程空闻内,同一进程所产生的线程共享同一用户内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。一个进程至少需要一个线程作为它的指令执行体,进程管理着资源(比如cpu、内存、文件等等)。而将线程分配到某个cpu上执行。
线程基本方法
- pthread_create 新建线程;
- pthread_self 获取线程id;
- pthread_exit 退出一个线程;
- pthread_join 等待线程结束
新建一个线程,打印hello world:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void* myfunc(void* args) {
printf("Hello world!\n");
return NULL;
}
int main() {
pthread_t th;
pthread_create(&th, NULL, myfunc, NULL);
// wait thread finish
pthread_join(th, NULL);
return 0;
}
获取线程id:
#include <pthread.h>
#include <stdio.h>
int main() {
printf("main thread id is: %lu\n", pthread_self());
return 0;
}
thread_t 定义在 /usr/include/bits/pthreadtypes.h
/* Thread identifiers. The structure of the attribute type is not
exposed on purpose. */
typedef unsigned long int pthread_t;
子线程获取自身id和主线程不同:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void* myfunc(void* args) {
printf("child thread id is: %lu\n", pthread_self());
printf("Hello world!\n");
return NULL;
}
int main() {
pthread_t th;
pthread_create(&th, NULL, myfunc, NULL);
printf("main thread id is: %lu\n", pthread_self());
// wait thread finish
pthread_join(th, NULL);
return 0;
}
比较线程id不同:
使用 pthread_equal函数
#include <pthread.h>
#include <stdio.h>
void* cmp_thread_id(void* args) {
pthread_t th = pthread_self();
pthread_t main_th = *(pthread_t*)args;
printf("ptread_equal return : %d whit args %lu and %lu\n", pthread_equal(th, main_th), th, main_th);
return NULL;
}
int main() {
pthread_t th;
pthread_t main_id = pthread_self();
pthread_create(&th, NULL, cmp_thread_id, (void*)&main_id);
// wait thread finish
pthread_join(th, NULL);
return 0;
}
新建两个线程,分别打印1-100:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
void* myfunc(void* args) {
int i;
for (i = 0; i < 100; i++) {
printf("Hello world %d from %s!\n", i, (char*)args);
// sleep 0.1s
sleep(0.1);
}
return NULL;
}
int main() {
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, myfunc, "thread1");
pthread_create(&thread2, NULL, myfunc, "thread2");
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
return 0;
}
线程终止:
#include <stdio.h>
#include <stdlib.h>
void* sum_func(void* args) {
int stop_num = *(int*)args;
for (int i = 0; i < 100; i++) {
if (i == stop_num) {
pthread_exit(args);
}
}
printf("never run here\n");
return NULL;
}
int main() {
pthread_t th1;
int stop_num = 9 * 9;
pthread_create(&th1, NULL, sum_func, (void*)&stop_num);
int ret_stop_num = 0;
int* p_t = &ret_stop_num;
pthread_join(th1, (void*)&p_t);
printf("thread ret val is %d\n", *p_t);
return 0;
}
注意:
void pthread_exit(void *retval);
参数retval不能是在线程调用的栈上的指针,因为线程结束后就不存在了。
The value pointed to by retval should not be located on the calling thread’s stack, since the contents of that stack are undefined after the thread terminates.
所以以下代码有问题:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void* sum_func(void* args) {
int stop_num = *(int*)args;
int ret_val = 0;
for (int i = 0; i < 100; i++) {
if (i == stop_num) {
ret_val = i;
pthread_exit((void*)&ret_val);
}
}
printf("never run here\n");
return NULL;
}
int main() {
pthread_t th1;
int stop_num = 9 * 9;
pthread_create(&th1, NULL, sum_func, (void*)&stop_num);
int ret_stop_num = 0;
int* p_t = &ret_stop_num;
pthread_join(th1, (void*)&p_t);
printf("thread ret val is %d\n", *p_t);
return 0;
}
会返回一个32764附近的值,具体原因未知。
使用chatgpt给的实例,看起来也有问题:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void *thread_func(void *arg)
{
int num = *(int *)arg;
printf("Thread %d started. will return num: %d\n", num, num);
pthread_exit((void *)&num);
}
int main()
{
pthread_t thread1, thread2;
int arg1 = 1, arg2 = 2;
pthread_create(&thread1, NULL, &thread_func, (void *)&arg1);
pthread_create(&thread2, NULL, &thread_func, (void *)&arg2);
void *res1, *res2;
pthread_join(thread1, &res1);
pthread_join(thread2, &res2);
printf("Thread 1 returned %d.\n", *(int *)res1);
printf("Thread 2 returned %d.\n", *(int *)res2);
return 0;
}
看起来同样有问题,返回值对不上。
运行结果如下:
>>> ./a.out
Thread 2 started. will return num: 2
Thread 1 started. will return num: 1
Thread 1 returned 0.
Thread 2 returned 0.
如果需要返回的话,可以使用malloc得到一个heap上的指针:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void* sum_func(void* args) {
int stop_num = *(int*)args;
int *ret_val = malloc(sizeof(int));
for (int i = 0; i < 100; i++) {
if (i == stop_num) {
*ret_val = i + 1;
pthread_exit((void*)ret_val);
}
}
printf("never run here\n");
return NULL;
}
int main() {
pthread_t th1;
int stop_num = 9 * 9;
pthread_create(&th1, NULL, sum_func, (void*)&stop_num);
int ret_stop_num = 0;
int* p_t = &ret_stop_num;
pthread_join(th1, (void*)&p_t);
printf("thread ret val is %d\n", *p_t);
free(p_t);
return 0;
}
线程分类
线程按照其调度者
- 用户级线程,主要解决的是上下文切换的问题,其调度过程由用户决定
- 内核级线程,由内核调度机制实现
用户级线程要绑定内核级线程运行,一个进程中的内核级线程会分配到固定的时间片,用户级线程分配的时间片以内核级线程为准。
当cpu分配给线程的时间片用完后但线程没有执行完毕,此时线程会从运行状态返回到就绪状态,将cpu让给其它线程使用。
简单应用
1-5000的数组 两个线程分别计算1-2500 2501-5000 的和,然后相加
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
int array[5000];
typedef struct {
int from;
int to;
int result;
}ARGS;
void* myfunc(void* args) {
int i;
ARGS* arg_n = (ARGS*)args;
int from_num = arg_n->from;
int to_num = arg_n->to;
for (i = from_num; i < to_num; i++) {
arg_n->result += array[i];
}
return NULL;
}
int main() {
int i;
for (i = 0; i < 5000; i++) {
array[i] = i + 1;
}
ARGS arg1 = {0, 2500, 0};
ARGS arg2 = {2500, 5000, 0};
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, myfunc, &arg1);
pthread_create(&thread2, NULL, myfunc, &arg2);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("%d + %d = %d \n", arg1.result, arg2.result, arg1.result + arg2.result);
return 0;
}