白山网站建设,郑州室内设计公司排行,做网站建议,个人可以做聊天网站备案吗目录
一、概念
二、互斥锁实现互斥
三、条件变量实现同步 银行家算法
生产者与消费者模型 一、概念
概念#xff1a;在多线程程序中#xff0c;如果涉及到了对共享资源的操作#xff0c;则有可能会导致数据二义性#xff0c;而线程安全就指的是#xff0c;就算对共享…目录
一、概念
二、互斥锁实现互斥
三、条件变量实现同步 银行家算法
生产者与消费者模型 一、概念
概念在多线程程序中如果涉及到了对共享资源的操作则有可能会导致数据二义性而线程安全就指的是就算对共享资源进行操作也不会导致数据二义 实现如何实现多线程中对共享资源的操作不会出问题 互斥通过同一时间对资源访问的唯一性保证访问安全 互斥的实现互斥锁读写锁、自旋锁... 同步通过条件控制让多执行对资源的获取更加合理 同步实现条件变量、信号量
二、互斥锁实现互斥
实现对共享资源的唯一访问
1互斥锁实现互斥的原理 本质就是一个1/0计数器通过0/1标记资源的访问状态0-不可访问、1-可访问 在访问资源之前进行加锁通过状态判断是否可访问不可访问则阻塞 在访问资源之后进行解锁将资源状态置为可访问状态唤醒其他阻塞的线程 也有另一种理解访问资源之前加锁获取锁资源-获取不到就阻塞 访问完毕解锁归还锁资源 多个线程想要实现互斥就必须访问同一个锁才可以也就意味着锁也是一个共享资源 互斥锁的操作本身必须是安全的互斥锁本身计数器的操作时原子操作
2互斥锁如何实现自身操作安全的原理 我们知道内存与cpu之间的数据传输当进行加锁操作时先将锁中的1置入cpu先将变量数据从内存加载到cpu然后才能从cpu中对数据进行处理转化为0然后在从cpu中将数据加载到内存指定位置完成将1替换为0
然而这样一种操作就会产生问题如果1从内存加载到cpu还没有将处理后的0加载回内存时这时切换其他线程运行访问到的锁资源仍然为1就会继续进行加锁这无疑是致命的。
所以对于锁资源本身来说计数器的操作必须是原子性的才可以。 有个指令类似于exchange功能是交换指定寄存器与内存中的数据 1、先将指定寄存器中的值修改为0, 2、将寄存器与内存中的数据进行互换 3、判断是否符合获取锁的条件或者说判断是否能够加锁 这样使用exchange指令就可以实现计数器操作为原子操作。
3接口
互斥锁类型变量 pthread_mutex_t
初始化互斥锁 int pthread_mutex_initpthread_mutex_t *mutex, pthread_mutexattr_t *attr mutex互斥锁变量的地址 attr互斥锁变量属性通常置NULL
访问资源前加锁 int pthread_mutex_lockpthread_mutex_t *mutex 阻塞加锁老实人 int pthread_mutex_trylockpthread_mutex_t *mutex 非阻塞加锁海王
访问资源后解锁 int pthread_mutex_unlockpthread_mutex_t *mutex
释放销毁互斥锁 int pthread_mutex_destroypthread_mutex_t *mutex pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER 这种初始化不需要销毁
4代码模拟
当不使用互斥锁线程之间对同一变量的访问情况如何
#includestdio.h
#includepthread.h
#includeunistd.hint ticket 100;
void *Scalper(void *arg)
{while(1){if(ticket 0){usleep(10);printf(%p我抢到了第 %d 号票\n,pthread_self(), ticket);ticket--;}else{printf(%p票完了我的工作结束了\n, pthread_self());break;}}return NULL;
}
int main()
{pthread_t tid[4];for(int i 0; i 4; i){int ret pthread_create(tid[i], NULL, Scalper, NULL);if(ret ! 0){perror(create error);return -1;}}for(int i 0; i 4; i){pthread_join(tid[i], NULL);}return 0;
}发现出现了很多意外情况重复、负数票号
为什么呢就是因为每个线程之间访问同一变量一个线程还正对变量进行操作的时候另一个线程也进行操作于是就发生了同一变量的多次出现。 使用互斥锁保护临界区
#includestdio.h
#includepthread.h
#includeunistd.hint ticket 100;
void *Scalper(void *arg)
{while(1){pthread_mutex_lock(arg); // 访问之前加锁if(ticket 0){usleep(10);printf(%p我抢到了第 %d 号票\n,pthread_self(), ticket);ticket--;pthread_mutex_unlock(arg); // 在所有有可能退出的地方解锁}else{printf(%p票完了我的工作结束了\n, pthread_self());pthread_mutex_unlock(arg); // 在所有有可能退出的地方解锁break;}usleep(1);}return NULL;
}
int main()
{pthread_mutex_t mutex; // 定义锁变量 mutexpthread_mutex_init(mutex, NULL); // 初始化锁资源pthread_t tid[4];for(int i 0; i 4; i){int ret pthread_create(tid[i], NULL, Scalper, mutex); // 注意锁资源通过线程创建第四个参数传入入口函数内if(ret ! 0){perror(create error);return -1;}}for(int i 0; i 4; i){pthread_join(tid[i], NULL);}return 0;
}5死锁
死锁是一种状态是一种因为资源争抢不当导致程序流程卡死无法继续向下推进的状态。
多个线程对锁资源的争抢使用不当导致程序流程卡死无法继续向下推进的状态
1、加锁之后没有释放就退出导致其他线程获取不到锁资源卡死
2、多锁使用时加锁顺序不当线程1加锁顺序为AB线程2加锁顺序为BA
发生死锁的必要条件 ① 互斥条件 同一时间一把锁只能被一个线程所获取到 ② 不可剥夺条件 一个线程加的锁只能自己释放其他线程无法释放 ③ 请求与保持 一个线程请求了A锁之后请求B锁如果请求不到就不会释放A锁 ④ 环路等待 线程1加了A锁后请求B锁线程2加了B锁后请求A锁
死锁的预防破坏死锁产生的必要条件 ① 和 ② 无法修改 写代码时要注意 1、线程之间的加解锁顺序尽量一致 -- 尽可能预防环路等待 2、采用非阻塞加锁如果加不上锁则把已经加上的锁释放 -- 破坏请求与保持 请求不到新的则释放已有的
避免银行家算法、死锁检测算法……
银行家算法http://t.csdn.cn/1YxZj
三、条件变量实现同步
1概念
同步通过条件控制保证资源访问的合理性
条件变量主要是一个pcb等待队列以及唤醒和阻塞线程的接口
原理 线程1 获取资源时进行判断如果线程不符合资源获取条件则调用阻塞接口进行阻塞 线程2 促使资源获取条件满足之后生产资源通过唤醒接口唤醒阻塞的线程
条件变量需要搭配互斥锁来使用
举例 顾客 与 厨师 消费者与生产者
顾客来到柜台看到柜台有饭则吃饭否则阻塞
厨师来到柜台看到柜台上没有饭则做饭否则阻塞
顾客 0、加锁关门 1、访问柜台有没有饭 有饭则吃饭没有饭就阻塞 这里阻塞时需要进行解锁否则厨师就无法访问柜台也就无法做饭产生死锁 阻塞则需先解锁再阻塞被唤醒之后再加锁 2、吃饭 3、吃完了再来一碗 唤醒厨师 4、解锁
厨师 0、加锁 1、访问柜台有没有饭 没饭则做饭有饭则阻塞 这里是有饭就需要解锁让顾客能够吃饭否则会死锁 阻塞需先解锁再阻塞被唤醒后加锁 2、做饭 3、做好了唤醒顾客 4、解锁
2接口
定义条件变量 pthread_cond_t 条件变量的变量类型
初始化条件变量 pthread_cond_t cond_init (pthread_cond_t *cond, pthread_condattr_t *attr);
阻塞接口条件变量是搭配互斥锁一起使用的就体现在阻塞这一步 int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) -- 阻塞接口 int pthread_cond_timewait(pthread_cond_t *cond, pthread_mutex_t *mutex, struct timespec *t ) -- 有时长限制的阻塞
唤醒接口 int pthread_cond_signalpthread_cond_t *cond 唤醒至少一个阻塞队列中的线程 int pthread_cond_broadcastpthread_cond_t *cond 唤醒等待队列中所有的线程
销毁接口 int pthread_cond_destroypthread_cond_t *cond
3模拟实现
#includestdio.h
#includepthread.hint counter 0; // 定义柜台状态 0——没饭 1——有饭
pthread_mutex_t mutex; // 初始化锁
pthread_cond_t cond; // 初始化条件变量void* customer(void *arg)
{while(1){pthread_mutex_lock(mutex); // 先加锁if(counter0){pthread_cond_wait(cond, mutex); // 没饭则解锁 并阻塞等待唤醒唤醒后加锁}printf(真好吃再来一碗\n);counter 0;pthread_cond_signal(cond); // 吃完了唤醒厨师做饭pthread_mutex_unlock(mutex); // 解锁}
}void* cook(void *arg)
{while(1){pthread_mutex_lock(mutex); // 加锁if(counter 1){pthread_cond_wait(cond, mutex); // 有饭则 解锁并阻塞等待唤醒唤醒后加锁}printf(你的饭好了\n);counter 1;pthread_cond_signal(cond); // 饭做好了唤醒顾客吃饭pthread_mutex_unlock(mutex); // 解锁}
}int main()
{pthread_mutex_init(mutex, NULL); // 初始化定义mutex 和 condpthread_cond_init(cond, NULL);pthread_t cook_tid; // 初始化定义线程ID顾客和厨师pthread_t cus_tid;int ret;ret pthread_create(cook_tid, NULL, cook, NULL); // 分别创建对应线程if(ret ! 0){perror(create error);return -1;}ret pthread_create(cus_tid, NULL, customer, NULL);if(ret ! 0){perror(create error);return -1;}pthread_join(cook_tid, NULL); // 执行线程等待pthread_join(cus_tid, NULL);pthread_mutex_destroy(mutex); // 执行mutex与cond的销毁pthread_cond_destroy(cond);
}实现四个顾客四个厨师 在同步实现的代码中如果存在多种角色就应该定义多个条件变量各自处于各自的pcb等待队列中。
#includestdio.h
#includepthread.hint counter 0;
pthread_mutex_t mutex;
pthread_cond_t cond_cus; // 使用不同的条件变量防止死锁
pthread_cond_t cond_cook;
void* customer(void *arg)
{while(1){pthread_mutex_lock(mutex);while(counter 0){pthread_cond_wait(cond_cus, mutex);}printf(真好吃再来一碗: %d\n,counter);counter--;pthread_cond_signal(cond_cook);pthread_mutex_unlock(mutex);}
}void* cook(void *arg)
{while(1){pthread_mutex_lock(mutex);while(counter 0){pthread_cond_wait(cond_cook, mutex);}printf(你的饭好了%d\n, counter);counter;pthread_cond_signal(cond_cus);pthread_mutex_unlock(mutex);}
}int main()
{pthread_mutex_init(mutex, NULL);pthread_cond_init(cond_cus, NULL);pthread_cond_init(cond_cook, NULL);pthread_t cook_tid[4]; // 定义四个顾客、四个厨师pthread_t cus_tid[4];int ret;for(int i 0; i 4; i) // 创建这八个线程{ret pthread_create(cook_tid[i], NULL, cook, NULL);if(ret ! 0){perror(create error);return -1;}ret pthread_create(cus_tid[i], NULL, customer, NULL);if(ret ! 0){perror(create error);return -1;}}pthread_join(cook_tid[0], NULL);pthread_join(cus_tid[0], NULL);pthread_mutex_destroy(mutex);pthread_cond_destroy(cond_cook);pthread_cond_destroy(cond_cus);
}生产者与消费者模型
生产者与消费者模型http://t.csdn.cn/GvZlZ