volatile
- volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存
- volatile 可以保证对特殊地址的稳定访问
- volatile可以保证每次对变量的访问都是从内存地址直接读取,而不是被编译器优化后可能导致的读错误
- volatile is for memory where reads and writes should not be optimized away. It’s a tool for working with special memory.
/* Compile code with optimization option */
#include <stdio.h>
int main(void)
{
const volatile int local = 10; // const int local = 10;
int *ptr = (int*) &local;
printf("Initial value of local : %d \n", local);
*ptr = 100;
printf("Modified value of local: %d \n", local);
return 0;
}
debug 和 release 版本下输出都是:
Initial value of local : 10
Modified value of local: 100
如果是: const int local = 10;
debug:
Initial value of local : 10
Modified value of local: 100
release(被优化出问题了):
Initial value of local : 10
Modified value of local: 10
Mutex
- mutex 互斥量, 实现多线程并发执行时对互斥资源线程安全的访问
- lll_lock 是mutex 关键宏, 其核心指令是 cmpxchgl ,汇编级别的CAS( compare and swap
#define lll_lock(futex, private) \
(void) \
({ int ignore1, ignore2; \
if (__builtin_constant_p (private) && (private) == LLL_PRIVATE) \
__asm __volatile ("cmpxchgl %1, %2\n\t" \
"jnz _L_lock_%=\n\t" \
".subsection 1\n\t" \
".type _L_lock_%=,@function\n" \
"_L_lock_%=:\n" \
"1:\tleal %2, %%ecx\n" \
"2:\tcall __lll_lock_wait_private\n" \
"3:\tjmp 18f\n" \
"4:\t.size _L_lock_%=, 4b-1b\n\t" \
".previous\n" \
LLL_STUB_UNWIND_INFO_3 \
"18:" \
: "=a" (ignore1), "=c" (ignore2), "=m" (futex) \
: "0" (0), "1" (1), "m" (futex), \
"i" (MULTIPLE_THREADS_OFFSET) \
: "memory"); \
示例
long num = 0;
std::mutex num_mutex;
void numplus() {
num_mutex.lock();
for (long i = 0; i < 1000000; ++i) {
num++;
}
num_mutex.unlock();
};
void numsub() {
num_mutex.lock();
for (long i = 0; i < 1000000; ++i) {
num--;
}
num_mutex.unlock();
}
int main() {
std::thread t1(numplus);
std::thread t2(numsub);
t1.join();
t2.join();
std::cout << num << std::endl;
}
互斥量管理 lock_guard
lock_guard 通常用来管理一个 std::mutex 类型的对象.
在构造函数中自动绑定它的互斥体并加锁,在析构函数中解锁,大大减少了死锁的风险.
- 创建即加锁,作用域结束自动析构并解锁,无需手工解锁
- 不能中途解锁,必须等作用域结束才解锁
#include <iostream>
#include <mutex>
#include <thread>
class Widget{
public:
Widget() = default;
~Widget() = default;
void fun(){
std::lock_guard<std::mutex> lock(lock_);
std::cout << "Widget::fun run" << std::endl;
}
private:
std::mutex lock_;
};
void TestThread1(Widget* w){
w->fun();
}
int main()
{
Widget* w = new Widget();
std::thread t1(&TestThread1, w);
t1.join();
return 0;
}
互斥量管理 unique_guard
除了lock_guard的功能外,提供了更多的member_function,相对来说更灵活一些。如: lock, try_lock, try_lock_for, try_lock_until, unlock
#include <mutex>
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>
int main()
{
std::mutex counter_mutex;
std::vector<std::thread> threads;
auto worker_task = [&](int id, int wait_seconds, int acquire_seconds) {
// wait for a few seconds before acquiring lock.
std::this_thread::sleep_for(std::chrono::seconds(wait_seconds));
std::unique_lock<std::mutex> lock(counter_mutex, std::defer_lock);
if (lock.try_lock()) {
std::cout << id << ", lock acquired.\n";
} else {
std::cout << id << ", failed acquiring lock.\n";
return;
}
// keep the lock for a while.
std::this_thread::sleep_for(std::chrono::seconds(acquire_seconds));
std::cout << id << ", releasing lock.\n";
lock.unlock();
};
threads.emplace_back(worker_task, 0, 0, 2);
threads.emplace_back(worker_task, 1, 1, 0);
threads.emplace_back(worker_task, 2, 3, 0);
for (auto &thread : threads) thread.join();
}
std::atomic
- std::atomic模板类可以使对象操作为原子操作,避免多线程竞争问题
- 底层实现是, 机器指令的CACHE LOCK 或 BUS LOCKBUS LOCK
- std::atomic is for data accessed from multiple threads without using mutexes. It’s a tool for writing concurrent software.
class Test
{
public:
Test() = default;
void CThreadFunc()
{
for (int i = 0; i < 10000; ++i)
{
//std::lock_guard<std::mutex> lck(Test::m_s_ivalue_mutex); //m_iValue需要加锁才可正常工作
m_iValue++;
m_atomic_value++;//不加锁,也可正常工作
}
}
void Start()
{
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i)
{
threads.push_back(std::thread(&Test::CThreadFunc, this));
}
for (auto& th : threads)
{
if (th.joinable())
{
th.join();
}
}
std::cout << "m_iValue:" << m_iValue << ", m_atomic_value:" << m_atomic_value << std::endl;
}
private:
int m_iValue = 0;
std::atomic<int> m_atomic_value = 0;//sta::atomic<T> 原子操作
static std::mutex m_s_ivalue_mutex;
};
semaphore
- 是一种轻量的同步原件,用于制约对共享资源的并发访问
- semaphore 是 mutex(init_MUTEX_LOCKED) 的包装, 也是基于CAS
int sem_init(sem_t *__sem, int __pshared, unsigned int __value)
- sem :指向信号量对象
- pshared : 指明信号量的类型。不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享。
- value : 指定信号量值的大小
- __value:代表初始化的__sem值。
int sem_post(sem_t *__sem)
__sem的值+1
sem_wait(sem_t* sem)
用来等待信号量的值大于0(value > 0),等待时该线程为阻塞状态
解除阻塞后sem值会减去1
sem_trywait(sem_t *sem)
sem_wait()的非阻塞版本,直接将sem的值减去1
sem_destroy(sem_t* sem)
释放信号量sem
sem_getvalue(sem_t* sem, int* valp)
获取信号量sem的值并且保存在valp中
#include <semaphore.h>
class FooBar
{
private:
int n;
protected:
sem_t FooReady;
sem_t BarReady;
public:
FooBar(int n)
{
this->n = n;
sem_init(&FooReady, 0, 0);
sem_init(&BarReady, 0, 1); //由于先输出foo所以BarReady设置为1
}
void foo(function<void()> printFoo)
{
for (int i = 0; i < n; i++)
{
sem_wait(&BarReady); //P(BarReady)
printFoo();
sem_post(&FooReady); //V(FooReady)
}
}
void bar(function<void()> printBar)
{
for (int i = 0; i < n; i++)
{
sem_wait(&FooReady); //P(FooReady)
printBar();
sem_post(&BarReady); //V(BarReady)
}
}
};