std::set::insert
不是多线程安全的,多个线程同时对同一个对象调用insert
其行为是未定义的(通常导致的结果是程序崩溃)。因此需要一种机制在此处对多个线程进行同步,保证任一时刻至多有一个线程在调用insert
函数。vector
里面,我们需要确保在同一时刻只有一个线程对vector
进行插入操作,所以我们必须为其加上一个锁,锁这个东西,依据我个人的理解,是一种互斥关系,有一个线程创建了这个互斥关系,那么当第二个线程再去创建同样的互斥关系的时候就会受到阻塞,就需要等待当前持有锁的线程来解锁,然后继续访问临界资源。mutex
头文件中定义了四种锁:mutex
:提供了核心的 lock()
和unlock()
方法,用来加解锁,以及当 mutex 不可用时就会返回的非阻塞方法 try_lock()
recursive_mutex
:依据名字可以看出这是递归锁,就是允许同一个线程对锁进行多重持有,多用于线程函数需要进行递归操作的情况timed_mutex
:时间锁,可以使用函数try_lock_for()
和try_lock_until()
来在特定的时长内持有mutex或持有锁到某个特定的时间点recursive_timed_mutex
:recursive_mutex
和 timed_mutex
的结合fun()
里面会怎么样呢?fun()
里面会怎么样呢?通过试验我们得知,如果将锁的定义放在线程函数里面的话,程序的输出结果会和不使用锁的情况是一样的。这是为什么呢,因为三个线程创建了三把锁,三把不一样的锁,然后各自加锁各自解锁,互不干涉。反观在全局变量中使用锁的情况,三个线程使用了同一把锁,所以才能正确地锁住线程。recursive_mutex
std::mutex mtx;
改为std::recursive_mutex mtx;
后程序即可不抛出异常,但是在写完这个程序之后,引发了我的深思,就是这个程序为什么需要锁,我一直找不到一个合适的理由,可能是仅仅将其作为一个介绍递归锁的一个实例罢了,同时我考虑了一种情况就是分配两个线程出来,第一个线程来求区间[0-10]以内的数列,第二个线程用来求区间[11-20]内的数列,但是这样做是毫无意义的,首先第二个线程必需依赖第一个线程所产生数列的结果,也就是说第二个线程必需等待第一个线程结束后,才能从set集合中取出用于求数列的充分条件,这样的设计显然是毫无意义的,至少我是这么认为的。std::recursive_mutex
,它允许一个线程对 mutex 多重持有。允许的最大持有次数并不确定,但当达到上限时,线程锁会抛出 std::system_error
错误。因此,要解决上面例子的错误,除了修改 addrange
令其不再调用 lock
和 unlock
之外,可以用 std::recursive_mutex
代替 mutex
。std::timed_mutex
和recursive_timed_mutex
try_lock_for
是设置一个超时时间,try_lock_until
是设置一个超时的时间点std::lock_guard
std::lock_guard
的例子std::unique_lock
std::unique_lock
里面实现了try_lock_for
和try_lock_until
两个函数,用来设置时间锁。std::lock_guard
和std::unique_lock
对于在构造的过程中是否加锁是可选的设置,C++提供了三种加锁的策略:std::lock
与std::try_lock
f:\dd\vctools\crt\crtw32\stdcpp\thr\mutex.c(51): mutex destroyed while busy
,我们可以得知发生了死锁,为什么会发生这种现象呢?std::this_thread::sleep_for(std::chrono::milliseconds(500));
让线程拿到锁之后休息一下,这样就大大提升了发生死锁的概率,如果去掉这行代码,发生死锁的现象将会是一个带有一定概率的事件,有时候会发生有时候不会发生。std::lock
来进行加锁,std::lock
会使用一种避免死锁的算法来对N个需要加锁的对象加锁,std::lock
可以接受N个参数