linux内核多线程
https://zhuanlan.zhihu.com/p/459579109 互斥锁、条件变量、读写锁、自旋锁、信号量
# 线程相关函数
头文件:#include <pthread.h> 注意:在编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非linux系统的默认库。
# 线程创建函数
//第一个参数为指向线程标识符的指针,传递一个pthread_t变量地址进来,用于保存新线程的tid(线程ID),可这样定义pthread_t thread1;
//第二个参数用来设置线程属性,如使用默认属性,则传NULL;
//第三个参数是线程运行函数的地址,即函数指针,指向新线程应该加载执行的函数模块;
//最后一个参数是运行函数的参数。
//若成功则返回0,否则返回出错编号。
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
# 线程等待函数
//以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回,如果线程已经结束,那么该函数会立即返回,并且指定的线程必须是joinable的。
//第二个参数为用户定义的指针,用来存储被等待线程的返回值(接收退出线程传递出的返回值)
//如果执行成功将返回0,失败则返回一个错误号。
int pthread_join(pthread_t thread, void **retval);
//如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返回值;如果thread线程是被别的线程调用pthread_cancel异常终止掉,retval所指向的单元里存放的是常数PTHREAD_CANCELED;如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数;如果对thread线程的终止状态不感兴趣,可以传NULL给retval参数。
# 线程退出函数
//参数: 线程退出时传递出的参数,可以是退出值或地址。如果线程不需要返回任何数据,将 retval 参数置为 NULL 即可。
//pthread_exit() 函数只会终止当前线程,不会影响进程中其它线程的执行。
void pthread_exit(void *retval);
#include <stdio.h>
#include <pthread.h>
//线程要执行的函数,arg 用来接收线程传递过来的数据
void *ThreadFun(void *arg)
{
//终止线程的执行,将“http://c.biancheng.net”返回
pthread_exit("http://c.biancheng.net"); //返回的字符串存储在常量区,并非当前线程的私有资源
printf("*****************");//此语句不会被线程执行
}
int main()
{
int res;
//创建一个空指针
void * thread_result;
//定义一个表示线程的变量
pthread_t myThread;
res = pthread_create(&myThread, NULL, ThreadFun, NULL);
if (res != 0) {
printf("线程创建失败");
return 0;
}
//等待 myThread 线程执行完成,并用 thread_result 指针接收该线程的返回值
res = pthread_join(myThread, &thread_result);
if (res != 0) {
printf("等待线程失败");
}
printf("%s", (char*)thread_result);
return 0;
}
//http://c.biancheng.net
# 线程终止函数
//如果 pthread_cancel() 函数成功地发送了 Cancel 信号,返回数字 0;反之如果发送失败,函数返回值为非零数。
//对于接收 Cancel 信号后结束执行的目标线程,等同于该线程自己执行如下语句:pthread_exit(PTHREAD_CANCELED);
int pthread_cancel(pthread_t thread);
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h> // sleep() 函数
//线程执行的函数
void * thread_Fun(void * arg) {
printf("新建线程开始执行\n");
sleep(10);
}
int main()
{
pthread_t myThread;
void * mess;
int value;
int res;
//创建 myThread 线程
res = pthread_create(&myThread, NULL, thread_Fun, NULL);
if (res != 0) {
printf("线程创建失败\n");
return 0;
}
sleep(1);
//向 myThread 线程发送 Cancel 信号
res = pthread_cancel(myThread);
if (res != 0) {
printf("终止 myThread 线程失败\n");
return 0;
}
//获取已终止线程的返回值
res = pthread_join(myThread, &mess);
if (res != 0) {
printf("等待线程失败\n");
return 0;
}
//如果线程被强制终止,其返回值为 PTHREAD_CANCELED
if (mess == PTHREAD_CANCELED) {
printf("myThread 线程被强制终止\n");
}
else {
printf("error\n");
}
return 0;
}
//新建线程开始执行
//myThread 线程被强制终止
# 互斥锁
多任务使用同一资源,引入互斥锁,来保证共享数据操作的完整性。 互斥锁(mutex),互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。 对互斥锁进行操作的函数:
#include <pthread.h>
#include <time.h>
// 初始化一个互斥锁。
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 对互斥锁上锁,若互斥锁已经上锁,则调用者一直阻塞,直到互斥锁解锁后再上锁。
int pthread_mutex_lock(pthread_mutex_t *mutex);
// 调用该函数时,若互斥锁未加锁,则上锁,返回 0;若互斥锁已加锁,则函数直接返回失败,即 EBUSY。
int pthread_mutex_trylock(pthread_mutex_t *mutex);
// 当线程试图获取一个已加锁的互斥量时,pthread_mutex_timedlock 互斥量原语允许绑定线程阻塞时间。即非阻塞加锁互斥量。
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,const struct timespec *restrict abs_timeout);
// 对指定的互斥锁解锁。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 销毁指定的一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。
int pthread_mutex_destroy(pthread_mutex_t *mutex);
# 创建互斥锁
pthread_mutex_t mtx; //这就定义了一个互斥锁,但如果想使用跟这个互斥锁还是不行,需要对它进行初始化操作
//第二个参数是NULL的话,互斥锁的属性会设置为默认属性
pthread_mutex_init(&mtx, NULL);
//还可以用下面的方式定义一个互斥锁
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
# 获取互斥锁
先拿到锁再执行需要互斥的操作,否则可能会导致多个线程都需要访问的数据结构不一致。
# 阻塞调用
//如果这个锁此时正在被其他线程调用,函数调用会进入阻塞状态,直道拿到锁才会返回。
pthread_mutex_lock(&mtx);
# 非阻塞调用
//当请求的锁正在被占用的时候,不会进入阻塞状态,而是立刻返回,并返回一个错误代码EBUSY,意思是有其他线程正在使用这个锁。
int err = pthread_mutex_trylock(&mtx);
if(0 != err){
if(EBUSY == err){
//the mutex could not be acquired because it was alreadu locked.
}
}
# 超时调用
//阻塞调用,增加一个超时时间
//时间相关函数(需笔记) https://blog.csdn.net/weixin_44880138/article/details/102605681
//clock()是以毫秒为单位,要正确输出时间差需要把它换成秒,因此需要除以CLOCKS_PER_SEC。
//阻塞等待线程锁,等待1s,1s后如果还没拿到锁的话,返回一个错误代码ETIMEDOUT,意思是超时了
struct timespec abs_timeout;
abs_timeout.tv_sec = time(NULL)+1;
abs_timeout.tv_nsec = 0;
int err = pthread_mutex_timedlock(&mtx, &abs_timeout);
if(0 != err){
if(EBUSY == err){
//the mutex could not be acquired because it was alreadu locked.
}
}
# 释放互斥锁
//用完互斥锁,一定要释放,不然下一个想要获得这个锁的线程,就只能阻塞
pthread_mutex_unlock(&mtx);
# 销毁互斥锁
//使用此函数销毁一个线程锁,线程锁的状态定义为“未定义”,对一个处于已初始化但未锁定状态的线程锁进行销毁是安全的。
pthread_mutex_destroy(&mtx);
# 条件变量
与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。
条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步 的一种机制,主要包括两个动作:
一个线程等待"条件变量的条件成立"而挂起; 另一个线程使 “条件成立”(给出条件成立信号)。
https://zhuanlan.zhihu.com/p/98466282
https://zhuanlan.zhihu.com/p/161010435
https://zhuanlan.zhihu.com/p/458856546