标准库支持互斥的形式包括互斥体(mutex)类和锁类。这些都可以实现同步。
互斥类:
与互斥类都定义在<mutex>中,所以使用的话, 需要加载这个头文件
非定时的互斥体类:
std::mutex; std::recursive_mutex;
都支持下面的方法:
- lock(): 调试线程将尝试获取锁,并且阻塞直到获得锁。这个方法会无限期的阻塞。如果希望设置线程阻塞的最长时间,应该使用定时的互斥体。
- try_lock(): 调用线程将尝试获得锁,如果当前锁被其他线程持有,这个调用会立即返回。如过成功获得锁,try_lock()返回true,否则返回false。
- unlock():释放由调用线程所持有的锁,使另一个线程能获得这个锁。
定时的互斥体类:
std::timed_mutex std::recursive_timed_mutex std::shared_timed_mutex
都支持下面的方法:支持普通的lock() , try_lock(),unlock()外还支持下面的方法:
- try_lock_for(rel_time) 调用线程尝试在给定的相对时间内获得这个锁。如果在给定的时间超时之后就不能获得这个锁,这个调用失败并返回false,在时间内获得这个锁,这个调用成功后会返回true。
- try_lock_until(abs_time):调用线程将尝试获得这个锁,直到系统时间等于或超过绝对时间,在时间内获得这个锁,这个调用成功后会返回true。如果在给定的时间超时之后就不能获得这个锁,失败并返回false
锁类
std::lock_guard; std::unique_lock; std::shared_lock
- lock_guard 是一个简单的锁。
- unique_lock是一个复杂的锁,它允许将获得锁的时间延长到计算需要时,远在声明之后。使用owns_lock()方法可以确定是否获得了锁。
- shared_lock 与unique_lock差不多,区别shared_lock 类在底层的共享互斥上调用与共享权的方法。获得的锁是共享锁。不是独占锁。
产考例子:就是第一个例子,改变下。
#include <mutex> //互斥的变量
class CounterE
{
public:
CounterE(int id, int numIterations)
:mId(id), mNumIterations(numIterations)
{
}
void operator ()() const
{
for (int i = 0; i < mNumIterations; ++i)
{
lock_guard<mutex> lock(mMutex);
cout << "Counter " << mId << " has value ";
cout << i << endl;
}
}
private:
int mId;
int mNumIterations;
static mutex mMutex;
};
mutex CounterE::mMutex;
void testCounterE()
{
//using uniform initialization syntax
thread t1{ CounterE{ 1,20 } };
//using named variable
CounterE c(2, 12);
thread t2(c);
//using temporary
thread t3(CounterE(3, 10));
//wait for threads to finish
t1.join();
t2.join();
t3.join();
}
结果:
Counter 1 has value 0
Counter 1 has value 1
Counter 1 has value 2
Counter 1 has value 3
Counter 1 has value 4
Counter 1 has value 5
Counter 1 has value 6
Counter 1 has value 7
Counter 1 has value 8
Counter 1 has value 9
Counter 1 has value 10
Counter 1 has value 11
Counter 1 has value 12
Counter 1 has value 13
Counter 1 has value 14
Counter 1 has value 15
Counter 1 has value 16
Counter 1 has value 17
Counter 1 has value 18
Counter 1 has value 19
Counter 3 has value 0
Counter 3 has value 1
Counter 3 has value 2
Counter 3 has value 3
Counter 3 has value 4
Counter 3 has value 5
Counter 3 has value 6
Counter 3 has value 7
Counter 3 has value 8
Counter 3 has value 9
Counter 2 has value 0
Counter 2 has value 1
Counter 2 has value 2
Counter 2 has value 3
Counter 2 has value 4
Counter 2 has value 5
Counter 2 has value 6
Counter 2 has value 7
Counter 2 has value 8
Counter 2 has value 9
Counter 2 has value 10
Counter 2 has value 11
然后发现这次不乱了,但是是执行完一个后,在执行的下个线程,这样不是我们想要的结果。
于是采用时间锁,与时间互斥量。
把类改成:
class CounterE
{
public:
CounterE(int id, int numIterations)
:mId(id), mNumIterations(numIterations)
{
}
void operator ()() const
{
for (int i = 0; i < mNumIterations; ++i)
{
// lock_guard<mutex> lock(mMutex);
unique_lock<timed_mutex> lock(mMutex, 200ms);
if (lock)
{
cout << "Counter " << mId << " has value ";
cout << i << endl;
}
}
}
private:
int mId;
int mNumIterations;
static timed_mutex mMutex;
};
timed_mutex CounterE::mMutex;
输出结果:
Counter 1 has value 0
Counter 2 has value 0
Counter 3 has value 0
Counter 1 has value 1
Counter 2 has value 1
Counter 2 has value 2
Counter 1 has value 2
Counter 3 has value 1
Counter 3 has value 2
Counter 1 has value 3
Counter 2 has value 3
Counter 2 has value 4
Counter 2 has value 5
Counter 2 has value 6
Counter 2 has value 7
Counter 3 has value 3
Counter 1 has value 4
Counter 2 has value 8
Counter 3 has value 4
Counter 1 has value 5
Counter 2 has value 9
Counter 3 has value 5
Counter 1 has value 6
Counter 2 has value 10
Counter 3 has value 6
Counter 1 has value 7
Counter 2 has value 11
Counter 3 has value 7
Counter 1 has value 8
Counter 3 has value 8
Counter 1 has value 9
Counter 3 has value 9
Counter 1 has value 10
Counter 1 has value 11
Counter 1 has value 12
Counter 1 has value 13
Counter 1 has value 14
Counter 1 has value 15
Counter 1 has value 16
Counter 1 has value 17
Counter 1 has value 18
Counter 1 has value 19
这样就不乱了,而且同时进行的线程,实现了最基本多线程同步的样子。也是大家想要的结果。
在线程中可以使用call_once,只执行一此后,才退出阻塞,让线程继续下去。
例子:
once_flag gOnceFlag;
void callNameOnce()
{
cout << "the only call once" << endl;
}
class CounterE
{
public:
CounterE(int id, int numIterations)
:mId(id), mNumIterations(numIterations)
{
}
void operator ()() const
{
for (int i = 0; i < mNumIterations; ++i)
{
// lock_guard<mutex> lock(mMutex);
call_once(gOnceFlag, callNameOnce);
unique_lock<timed_mutex> lock(mMutex, 200ms);
if (lock)
{
cout << "Counter " << mId << " has value ";
cout << i << endl;
}
}
}
private:
int mId;
int mNumIterations;
static timed_mutex mMutex;
};
timed_mutex CounterE::mMutex;
输出结果:
the only call once
Counter 3 has value 0
Counter 2 has value 0
Counter 1 has value 0
Counter 3 has value 1
Counter 3 has value 2
Counter 1 has value 1
Counter 2 has value 1
Counter 3 has value 3
Counter 1 has value 2
Counter 2 has value 2
Counter 3 has value 4
Counter 1 has value 3
Counter 2 has value 3
Counter 3 has value 5
Counter 1 has value 4
Counter 2 has value 4
Counter 3 has value 6
Counter 1 has value 5
Counter 2 has value 5
Counter 3 has value 7
Counter 1 has value 6
Counter 2 has value 6
Counter 2 has value 7
Counter 1 has value 7
Counter 3 has value 8
Counter 2 has value 8
Counter 1 has value 8
Counter 3 has value 9
Counter 2 has value 9
Counter 1 has value 9
Counter 2 has value 10
Counter 1 has value 10
Counter 2 has value 11
Counter 1 has value 11
Counter 1 has value 12
Counter 1 has value 13
Counter 1 has value 14
Counter 1 has value 15
Counter 1 has value 16
Counter 1 has value 17
Counter 1 has value 18
Counter 1 has value 19
我们惊奇的发现只执行了一次,其实就是让他执行一次,然后就不执行了。是不是非常有意思呢,第一章出现的问题也算解决了。