线程的概念
①线程是拥有一个线程ID(类型是pthread_t),一个程序计数器(前面有一篇博文介绍过),一组寄存器,一个堆栈内存。
②线程属于进程的一部分,进程是系统分配资源的单位,而线程是系统的执行调度的单位,我们可以把线程理解为一个程序执行的单位,以前的进程也是线程,原因是程序就只有一个主函数(也叫主线程),现在有多线程,指的是多个执行体,线程与的exec函数开创子进程类似,不过线程是共享进程的资源,而exec开创的函数只会共享部分资源,比如进程打开的文件描述符。
线程创建的步骤
①动态创建函数
pthread_create(&ID,NULL,(void *)(&函数名),(void *)(&变量名));
第一个参数是存放新创建线程的ID,类型是pthread_t
第二个参数是NULL,表示使用默认线程属性,也可以时其他的
第三个参数是线程创建后所要执行的函数,只需把函数名填入上述格式中
第三个参数是传入线程函数的参数,只能有一个如果需要多个则使用结构体
②在线程中可以获取线程的ID使用
pthread_t pthread_self();//返回值是线程的ID
也可以从线程中退出,但是不能使用exit(int status)或者使用_exit(int status)这两个函数,他俩会让整个进程结束。使用如下函数结束线程。
pthread_exit(void *退出码);
一旦创建线程,他和主线程不一定谁先结束,所以可以使用如下函数,让子线程先结束,主线程才会结束
pthread_join(子线程ID(pthread_t *ID),NULL);
此时父线程会挂起,直到子线程执行完毕。
还有一个函数如下,用来比较两个线程是否是同一个线程
pthread_t pthread_equal(pthread_t *线程1的名字,pthread_t *线程2的名字)
线程同步
因为线程是共享进程的资源,当一个线程对数据修改,一个线程对数据读的时候就需要保证数据的实时性和有效性,所以需要同步,实现同步的方式有三种,互斥锁,条件变量,信号量,其中互斥锁和条件变量配合紧密。
互斥锁
互斥锁使用步骤即关键函数
- 首先声明一个保存互斥锁ID的变量,变量类型是pthread_mutex_t
pthread_mutex_t ID;
- 初始化互斥锁
pthread_mutex_init(&ID,NULL);
- 加锁
pthread_mutex_lock(&ID);
//当该锁已经被加锁,这里就会陷入阻塞状态
//如果相同线程对同一个变量再次锁定,就会造成死锁状态
pthread_mutex_trylock(&ID);
//尝试加锁,当该锁已经被锁住的时候,立刻返回,不阻塞,用perror();打印原因是EBUSY
- 解锁
pthread_mute_unlock(&ID);
//这里需要注意解锁和加锁是配对的,不能当前线程加的锁让其他线程解锁
- 销毁锁
//当该互斥锁不用的时候则可以销毁该互斥锁
pthread_mutex_destroy(&ID);
条件变量
条件变量可以用于控制线程执行的先后顺序及同步,他与互斥锁配合使用,使用如下
- 首先创建条件变量
pthread_cond_t cond_t;
- 条件变量的初始化
pthread_cond_init(&cond_t,NULL);
- 运行一个被控制线程,被控制线程中先上锁,然后一个while循环,条件保持为真,然后使用使用条件变量进入等待状态。
pthread_mutex_lock(&ID);//先上锁
pthread_cond_wait(&cond_t,&ID)//此时会阻塞,并且会解锁,销毁这把锁
//当收到pthread_cond_signal()函数的信号时,就会重新创建这把锁,并加锁,退出函数,并重新检查条件是否满足。
- 另外运行控制线程,使用相应的函数改变条件并使得被控制线程唤醒检查刚才的条件。执行线程
pthread_cond_wait(&cond_t);//改变条件后,向被控制线程发送信号,唤起该线程。