我原以为如果timed_mutex.try_lock_for能成功地用于所有调用线程,比如:
timed_mutex tm;
atomic<int> atTm(0);
void tTMutex(int i) {
this_thread::sleep_for(chrono::microseconds(200 * i));
if (tm.try_lock_for(chrono::milliseconds(200 * i))) {
atTm.fetch_add(1);
}
}
int main() {
thread t[3];
for (size_t i = 0; i < size(t); ++i) {
t[i] = thread(tTMutex, i);
}
ready = true;
for (size_t i = 0; i < size(t); ++i) {
t[i].join(); // seems only 1 thread increased atTm
}
cout << atTm;
return 0;
}
它打印";1〃;,而我期望它可以打印";3〃;。只要3个线程在不同的时间调用tm.try_lock_for
,我认为它们都应该成功返回,但事实并非如此:这是什么原因?
我稍微改变了我的程序,使用普通的mutex
而不是timed_mutex
,如下所示:
mutex mAllWait; // change 1
atomic<int> atAll(0);
void tMutex() {
unique_lock<mutex> lk(mAllWait); // change 2
this_thread::sleep_for(chrono::milliseconds(200));
atAll.fetch_add(1);
}
int main() {
thread t[3];
for (size_t i = 0; i < size(t); ++i) {
t[i] = thread(tMutex);
}
ready = true;
for (size_t i = 0; i < size(t); ++i) {
t[i].join(); // all threads can increase atAll
}
cout << atAll;
return 0;
}
它打印3
,正如我所料。那么这两个程序的核心区别是什么呢?
在第一个版本中获得锁后,您没有解锁互斥锁:
if (tm.try_lock_for(chrono::milliseconds(200 * i))) {
atTm.fetch_add(1);
tm.unlock(); // missing
}
所以只有第一个线程能够获得它。这就是为什么std::unique_lock
应该是优选的。它在析构函数中解锁。它还接受std::timed_mutex
作为模板参数,并具有良好的try_lock_for
成员函数。
除此之外:
不能保证try_lock_for
会等待您指定的时间。它可能在达到时间限制之前被错误地唤醒并返回false
。
当您开始等待时,或者在获取互斥之前的任何其他时间,或者即使当前任何其他线程都没有获取互斥,这种情况也可能立即发生。那么就不会在该线程中增加atTm
。
当然,也有可能try_lock_for
调用实际上会超时,因此您不会增加,但我认为,只要您不在严重过载的系统上运行,这就不应该发生。
在循环中运行try_lock_for
调用,如果您关心的话,请检查时间是否实际已超过。这可能比使用try_lock_until
更简单。