线程有点懵逼
线程之前函数回顾以及总结部分(对不清楚的问题再思考)
cpp
线程控制原语 进程控制原语
pthread_create(); fork();
pthread_self(); getpid();
pthread_exit(); exit();
pthread_join(); wait()/waitpid();
pthread_cancel(); kill();
pthread_detach();
这里写一下昨天的join函数,那块有点难理解。
cpp
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
struct thrd{
int var ;
char str[256];
};
void* func(void * arg)
{
struct thrd *th; // 这里不能直接使用结构体,因为这是在栈上的
th = malloc(sizeof(struct thrd)); // 这边需要在堆区分配内存,如果不使用堆区会发生错误,因为当线程执行完之后,栈上的内存会释放,如果返回栈上的内容会非法访问。
th->val = 200;
strcpy(th->str , "hello pthread_join");
return (void*)th;
}
int main(int argc , char* argv[])
{
pthread_t tid ;
struct thrd *retval;
int ret = pthread_create(&tid , NULL , func , NULL);
if(ret != 0){
fprintf(stderr , "pthread_create error : %s\n" , strerror(ret));
exit(1);
}
ret = pthread_join(tid , (void**)&retval);
if(ret != 0){
fprintf(stderr , "pthread_join error : %s\n" , strerror(ret));
exit(1);
}
printf("child thread exit with var = %d , str = %s\n " , retval->val , retval->str);
pthread_exit(NULL);
}
线程当中用perror没有用,线程会直接返回errno,所以在线程中错误一般使用下面这个格式:
cpp
if(ret != 0){
fprintf(stderr , "pthread_create error : %s\n" , strerror(ret));
exit(1);
}
线程分离之后,子线程结束会自动回收,自动清理PCB,无需回收
cpp
ret = pthread_detach(tid)
线程属性
初始化线程属性
cpp
int pthread_attr_init(pthread_attr_t* attr);
成功返回0
失败errno
fprintf(stderr , "pthread_xxx error:%s\n" , strerror(ret));
销毁线程属性所占用的资源
cpp
int pthread_attr_destroy(pthread_attr_t* attr);
成功返回0
失败errno
fprintf(strerr , "pthread_attr_destroy error:%s\n" , strerror(ret));
设置线程分离
cpp
int pthread_attr_setdetachstate(pthread_attr_t* attr , int detachstate);
参数:attr:已初始化的线程属性
detachstate : PTHREAD_CREATE_DETACHED(分离线程)
PTHRED_CREATE_JOINABLE(非分离线程)
cpp
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
void* tfn(void* arg){
printf("thread:pid = %d , tid = %lu\n" , getpid() , pthread_self());
return NULL;
}
int main(int argc , char *argv[])
{
pthread_t tid ;
//tid = pthread_self();
pthread_attr_t attr;
int ret = pthread_attr_init(&attr);
if(ret != 0){
fprintf(stderr , "pthread_attr_init error:%s/n" , strerror(ret));
exit(1);
}
ret = pthread_attr_setdetachstate(&attr , PTHREAD_CREATE_DETACHED);//设置线>程属性为分离属性
if(ret != 0){
fprintf(stderr , "pthread_attr_setdetachstate error:%s/n" , strerror(ret));
exit(1);
}
ret = pthread_create(&tid , &attr , tfn , NULL);
if(ret != 0){
fprintf(stderr , "pthread_create error:%s/n" , strerror(ret));
exit(1);
}
ret = pthread_attr_destroy(&attr);
if(ret != 0){
fprintf(stderr , "pthread_attr_destory error:%s/n" , strerror(ret));
exit(1);
}
ret = pthread_join(tid , NULL); // 如何查看是否是分离状态,不使用那个函数,>函数太长了,可以使用join函数,如果join函数回收失败出现错误,就表示已经为线程分离
。
if(ret != 0){
fprintf(stderr , "pthread_join error:%s/n" , strerror(ret));
exit(1);
}
printf("main:pid = %d , tid = %lu\n" , getpid() , pthread_self());
pthread_exit(NULL);
}
线程使用注意事项
1、主线程退出其他线程不退出,主线程要使用pthread_exit()。
2、避免僵尸线程。
3、malloc和mmap申请的内存可以被其他线程释放。
4、避免在多线程模型中使用fork,子进程中只有调用fork的线程存在,其余均被退出。
5、信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制。
线程同步
同步:协同,讲究先后次序。
协同步调,对公共区域数据按序访问,防止数据混乱,产生与时间有关的错误。
锁的使用
建议锁,对公共数据进行保护,所有进程应该在访问公共数据之前先拿锁再访问,但锁本身不具备强制性。
互斥锁/互斥量mutex

使用mutex(互斥量、互斥锁)一般步骤
cpp
1、pthread_mutex_t lock; 创建锁
2、pthread_mutex_init(); 初始化锁
3、pthread_mutex_lock(); 加锁
4、访问共享数据
5、pthread_mutex_unlock(); 解锁
6、pthread_mutex_destory(); 销毁锁
上述函数成功返回0,失败返回errno
注意事项:尽量保证锁的粒度
cpp
restrict关键字:
用来限定指针变量,被该关键字限定的指针变量所指向的内存操作,必须由本指针完成
int pthread_mutex_init(pthread_mutex_t *restrict mutex , const pthread_mutexattr_t *restrict attr);
今天没学完,还要写论文