pthread共享内存编程学习(1) – 基本语法学习
文章目录
本次学习以课本《并行程序设计导论》第四章为主,并且学习其他dl们的博客,做一些学习记录。
1.hello pthread(基础小例子)
例子
注释里有对程序的解释,和我对不懂的地方的注解
编译语句:
gcc -g -Wall -o test test.c
# -g debug模式
# -Wall 开启所有警告
#-o 指定输出文件名,该文件为可执行文件,不加-o会默认生成a.out
#-c (compile)只编译生成中间同名目标文件,不链接
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
//全局变量 被所有线程所共享
//在函数中声明的局部变量由执行该函数的线程所私有
int thread_count;
//void的字面意思是“无类型”,void *则为“无类型指针”,void *可以指向任何类型的数据。
void* Hello(void* rank);
int main(int argc, char** argv) {
long thread;
pthread_t* thread_handles;
//strtol 将字符串转换为long int;由stdlib.h声明
//参数:指向被转换的字符串的指针,指向字符串中第一个无效字符的指针,进制
thread_count = strtol(argv[1], NULL, 10);
printf("%daaa\n",thread_count);
//为每个线程的pthread_t对象分配内存
//pthread_t数据结构用来存储线程的专有信息
//pthread_t是不透明对象,对象中存储的数据都是系统绑定的,用户级代码无法直接访问里面的数据
/
thread_handles = malloc(thread_count*sizeof(pthread_t));
for(thread = 0; thread < thread_count; thread++) {
//pthread_create 生成线程
// 参数:指向对应的pthread_t对象, ,线程要运行的函数,指针,指向要传入的参数
// 返回值表示多线程调用是否有错误
pthread_create(&thread_handles[thread], NULL, Hello, (void*) thread);
}
printf("hello from the main thread\n");
for(thread = 0; thread < thread_count; thread++) {
//停止线程
pthread_join(thread_handles[thread], NULL);
}
free(thread_handles);
return 0;
}
void* Hello(void* rank) {
long my_rank = (long) rank;
printf("Hello from thread %ld of %d\n",my_rank, thread_count);
}
pthtread基本知识
-
在pthread程序中,全局变量被所有线程所共享,而在函数中声明的局部变量则通常由执行该函数的线程所私有。
-
pthread_t:类型名称
-
当定义好一个线程后,需要显式的为pthread_t对象声明内存
pthread_t thread; thread = malloc(sizeof(pthread_t));
-
不透明的对象,对象中存储的数据都是系统绑定的,用户级代码无法直接访问到里面的数据
-
-
pthread_create():生成线程
-
参数列表
-
pthread_t* //out 指向pthread_t对象的指针
-
第二个参数不需要,直接填入NULL
-
void* //in 线程要执行的函数
-
void* //in 函数的参数(可以传入结构体)
在例子中,通过将int变量强转成void*,在函数中再将void*强转为int
-
-
-
pthread_join():停止进程
- 参数列表
- pthread_t // in 等待的pthread对象
- 返回值,填入NULL即可
- 以阻塞的方式等待thread指定的线程结束,然后返回。
- 如果没有join函数,主线程不会等待子线程完成返回,会直接返回。
- 参数列表
-
free:释放线程
小问题记录
在刚开始写代码的时候,运行结果会报一个段错误的提醒·
Segmentation fault(core dumped) 段错误
排查后发现是自己手残写错了一个地方:malloc的时候,将sizeof(pthread_t)写成sizeof(thread_count),所以会产生分配的空间小于应分配的空间。
这个问题本身没啥,但是有个问题比较奇怪:没有被分配空间的线程可以执行对应的函数(即ptread_create函数没问题),但是在pthread_join函数的时候会报错:循环到没分配内存的线程时报错。
考虑什么情况下会报段错误:段错误应该就是访问了不可访问的内存,这个内存区要么是不存在的,要么是受到系统保护的,还有可能是缺少文件或者文件损坏。
因为没有阅读phread_join的源代码(比较懒,而且感觉应该看不懂),所以这里大胆的推断一下(假设有8个线程,但是只分配了前4个的空间):
- 没有分配的后四个pthread_t对象,eg: thread_handles[6],他指向的内存空间并没有被实际被分配出来,也就是指向了一个不属于他的内存;但是雀氏存在一个pthtread_t对象。
- 执行pthread_create方法的时候,不需要涉及到该线程对象所指向的内存空间,所以可以正常执行(说实话挺离谱的)
- 执行pthread_join方法的时候,要释放线程之前被分配的内存空间;问题来了,这些线程之前并没有被分配空间,也就是函数认为这块空间是属于线程的,想要释放它,但实际上并不属于线程,所以造成了 访问了不可访问的内存(应该是被保护了8),所以报错。
所以最后还有一个小问题没有确定:为什么没有被malloc的pthread_t也能够执行pthread_create函数来执行这个线程。(以后解答 or 有大佬看到这个问题解答一下)
2.互斥量 mutex
例子
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t mutex;//互斥量
//全局变量 被所有线程所共享
//在函数中声明的局部变量由执行该函数的线程所私有
int thread_count;
void* Hello(void* rank);
int main(int argc, char** argv) {
long thread;
pthread_t* thread_handles;
pthread_mutex_init(&mutex, NULL);//初始化互斥量
thread_count = strtol(argv[1], NULL, 10);
thread_handles = malloc(thread_count*sizeof(pthread_t));
for(thread = 0; thread < thread_count; thread++) {
pthread_create(&thread_handles[thread], NULL, Hello, (void*) thread);
}
printf("hello from the main thread\n");
for(thread = 0; thread < thread_count; thread++) {
pthread_join(thread_handles[thread], NULL);
}
//销毁互斥量
pthread_mutex_destroy(&mutex);
free(thread_handles);
return 0;
}
void* Hello(void* rank) {
long my_rank = (long) rank;
//printf("Hello from thread %ld of %d\n",my_rank, thread_count);
//申请互斥锁
pthread_mutex_lock(&mutex);
printf("mutex from thread %ld of %d\n",my_rank, thread_count);
//释放互斥锁
pthread_mutex_unlock(&mutex);
}
互斥量基本知识
-
pthread_mutex_t:类型名称
-
pthread_mutex_init():在使用互斥量之前,需要对其进行初始化
- 参数:
- pthread_mutex_t*
- null
- 参数:
-
pthread_mutex_destory():当Pthread程序使用完互斥量后
- 参数:
- pthread_mutex_t*
- 参数:
-
pthread_mutex_lock():获得临界区的访问权
- 参数:pthread_mutex_t*
-
pthread_mutex_unlock():退出临界区
- 参数:pthread_mutex_t*
互斥量 vs 忙等待
忙等待:通俗的说就是一个while死循环。
define flag = true
while(flag != true) ;
flage = false;
...
flag = true;
直观的看互斥量和忙等待,观感上二者相差不大。
区别:互斥量相比于忙等待对CPU有更好的利用率
互斥量:当互斥量已经被lock以后,其他线程想要进入临界区,调用了lock函数后,会阻塞并等待,直到第一个线程离开临界区。
注意:Pthread无法保证线程按照调用pthread_mutex_t的顺序获得进入临界区的锁,但是在设定中,只用有限个线程在尝试获得锁的所有权,最终每一个进程都会获得锁。
线程状态:https://blog.csdn.net/xingjing1226/article/details/81977129
阻塞并等待:线程会释放CPU的资源,进入等待队列,直到等待资源的释放。
总结:比较使用忙等待和互斥量的程序性能,当线程个数少于CPU核数时,发现二者的执行时间并没有很大的差别;当线程数多余CPU核数时,则使用互斥量的性能就会优于忙等待。
re:使用互斥量的程序,当某个线程占据了一个核,他要进入临界区的时候,发现没有权限,那么会被阻塞并等待,并且释放占据的CPU资源,这些CPU资源会被其他线程所使用;使用忙等待的线程,发现没有权限后,会一直进行while死循环,在这期间一直占用CPU,该CPU资源相当于处于闲置状态,所以利用率低。
参考博客
线程5中状态详解 https://blog.csdn.net/xingjing1226/article/details/81977129
段错误(核心已转储)(core dumped)问题的分析方法 https://blog.csdn.net/weixin_40877924/article/details/108762118
主要内容都是书里的