一个线程通知另一个线程需要多长时间?

How long does it take for one thread to notify another thread?

提问人:user3100212 提问时间:11/10/2023 最后编辑:HolyBlackCatuser3100212 更新时间:11/10/2023 访问量:118

问:

我想测量条件变量通知另一个等待线程所需的时间。我写了这个例子来尝试捕捉时差:

bool ready{false};
mutex m;
condition_variable cv;
decltype(chrono::steady_clock::now()) t1;
decltype(chrono::steady_clock::now()) t2;

void notifying_thread() {
    {
        lock_guard<mutex> lg{m};
        ready = true;
    }
    t1 = chrono::steady_clock::now();
    cv.notify_one();
}

void waiting_thread() {
    unique_lock<mutex> ul{m};
    cv.wait(ul, [] { return ready; });
    t2 = chrono::steady_clock::now();
}

int main() {
    auto f1{async(notifying_thread)};
    auto f2{async(waiting_thread)};
    f1.get();
    f2.get();
    cout << chrono::duration_cast<chrono::microseconds>(t2 - t1).count() << " µs\n";
    return 0;
}

我做对了吗?我把我的 s 放在正确的地方吗?平均时间为~25 μs,但有时甚至是负数。怎么可能是负面的?不应该总是在之前开始吗?发布政策是否相关?chrono::steady_clock::now()t1t2async

C 多线程 ++11 并发 C++-chrono

评论

1赞 463035818_is_not_an_ai 11/10/2023
我会在调用 之前和之后获取一个时间戳。它不会阻止,但也不会立即阻止。此外,我认为您可能会在已经设置但尚未调用的短时间窗口内出现虚假唤醒。如果发生这种情况,时间可以预期为负数notify_oneready = true;cv.notify_one();
1赞 463035818_is_not_an_ai 11/10/2023
如果在您测量该时间之前开始“很长时间”,而不是通知所需的时间。notifying_threadwaiting_thread
0赞 user3100212 11/10/2023
@molbdnilo我不同意。我认为应该保证它的时间戳早于因为直到等待返回 true 之后才能设置,并且此时已经设置。独立时间线是什么意思?t1t2t2t1
1赞 463035818_is_not_an_ai 11/10/2023
正如我之前提到的,你错过了虚假的觉醒。可能会发生虚假解锁的情况。这就是谓词的用途:它只在为真时解锁,但这种情况发生在之前waitreadyt1 = chrono::steady_clock::now(); cv.notify_one();
0赞 Daniel Langr 11/10/2023
@user3100212 t2 cannot be set until after wait returns true — 您错过了 (1) 未在 (2) 中调用的可能情况,甚至没有调用一次。有关详细信息,请参阅答案。waitwait

答:

3赞 463035818_is_not_an_ai 11/10/2023 #1

你的推理中缺少虚假的觉醒。带有谓词的 std::condition_variable::wait 重载等效于

while (!stop_waiting())
{
    wait(lock);
}

wait(lock)即使未通知条件变量,也可以返回。这就是谓词的用途。当有虚假唤醒时,你会继续循环,直到条件被满足。

由于虚假唤醒,可能会发生在其他线程调用之前但在设置之后解锁的情况。在这种情况下,可以早于 .wait(lock)notify_oneready = truet2t1

此外,正如 DanielLangr 在评论中指出的那样,当条件在进入上述循环之前就已经完全填充时,则永远不会被调用。同样在这种情况下,可以早于 .wait(lock)t2t1

此外,这两个线程不会立即启动。如果开始和开始的时间过后,那么你基本上是在测量该时间,而不是通知条件变量所需的时间。notifying_threadwaiting_thread

评论

1赞 Daniel Langr 11/10/2023
AFAIK,虚假唤醒相对罕见。但在这里,也可能发生的情况是,在到达循环之前先设置标志。因此,根本不是调用。如果发生这种情况,这些时间测量值将完全不同步。readywhilewait
1赞 463035818_is_not_an_ai 11/10/2023
@DanielLangr好点子。我过于关注一个反例,而错过了一个更明显的反例