概述
描述
在开发某段代码时,需要加互斥锁,这里进行相关知识的总结
基础知识
1. 原子性与原子操作
原子性
原子性是指一个操作是不可中断的。
(这里有一个非我专业领域的名词, CPU指令集中常见的一个指令:CAS。它完成两个操作,一个比较,一个交换,后一个完不完成依赖于前一个操作的结果,从逻辑上说,它们是两个操作,可是它们特意被实现为不可打断的。这就是一个经典的原子指令)
原子操作
原子操作是指不会被线程调度机制打断的操作。这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程) 。原子操作是不可分割的,在执行完毕之前不会被任何其它任务或事件中断
进一步举例子
int a = 10;
a++;
int b=a;
a = a+1;
上述只有第一个是原子操作,剩下三个都不是原子操作。比如a++
需要三步,读取a的值,值+1,再把值赋给a。
为什么要提原子性
因为总有一些代码,我想让它被某个线程执行的时候,我不想让其他线程来改变我这段代码中间的变量值。这种想法专业的说,就是,我想让这段代码具有原子性,称为一个原子操作。
这也是我为什么要先介绍原子性的原因。
2. 互斥锁
互斥锁是什么?
互斥,是指一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。
互斥锁,是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。
互斥锁也可以称为互斥量。互斥量是一个可以处于两态之一的变量:解锁和加锁
互斥锁的特点
-
原子性:把一对互斥锁定为一个原子操作,这意味着操作系统(或pthread函数库)保证了如果一个线程锁定了一个互斥量,没有其他线程在同一时间可以成功锁定这个互斥量;(我对这段代码加锁后,其他线程都得等我这个线程运行完这段代码再说)
-
唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以锁定这个互斥量;(我对这段代码加锁后,我这个线程不解锁,其他线程都不可以加锁)
-
非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。(我对这个代码加锁后,其他线程还想对它加锁时,得等我解锁了,你才能加锁)
互斥锁的作用
某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
代码
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
std::mutex my_mutex;
int g_count = 0;
void Counter() {
my_mutex.lock(); // 加锁
int i = ++g_count;
std::cout << "count: " << i << std::endl;
my_mutex.unlock(); // 解锁
}
int main() {
int size = 4;
// 创建一组线程。
std::vector<std::thread> my_thread;
my_thread.reserve(size);
for (int i = 0; i < size; i++) {
my_thread.emplace_back(&Counter);
}
// 等待所有线程结束。
for (std::thread& t : my_thread) {
t.join();
}
return 0;
}
输出结果会是
count: 1
count: 2
count: 3
count: 4
如果不加锁,每次的结果都会是不同的,而且是乱的,举个例子:
count: 2
count: count: 3
count: 4
1
关于代码中出现的一些写法
1. for (std::thread& t : my_thread)
for (std::thread& t : my_thread)
这个格式是C++11的for循环新形式
C++11现在有好几种形式来完成for循环,它们都可以实现遍历,功能是一样的,这里列举在下面
- 传统for循环
#include<iostream> #include<vector> int main() { std::vector<int> arr; arr.push_back(1); arr.push_back(2); for (auto it = arr.begin(); it != arr.end(); it++) { std::cout << *it << std::endl; } return 0; }
- std::for_each形式
#include<algorithm> #include<iostream> #include<vector> void func(int n) { std::cout << n << std::endl; } int main() { std::vector<int> arr; arr.push_back(1); arr.push_back(2); std::for_each(arr.begin(), arr.end(), func); return 0; }
- C++11新型for循环
#include<iostream> #include<vector> int main() { std::vector<int> arr; arr.push_back(1); arr.push_back(2); for (auto n : arr) { std::cout << n << std::endl; } return 0; }
2. reserve() 和 emplace_back()
这个请查看我的另外一篇博客
最后
以上就是发嗲泥猴桃为你收集整理的互斥锁的相关知识及应用描述基础知识代码关于代码中出现的一些写法的全部内容,希望文章能够帮你解决互斥锁的相关知识及应用描述基础知识代码关于代码中出现的一些写法所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复