繁体   English   中英

为什么它不起作用? 简单的多线程示例

[英]Why it doesn't work? Simple multithreading example

你能帮我理解为什么这段代码会冻结程序吗?

#include <iostream>
#include <thread>
#include <mutex>

using namespace std;

int i = 0;

mutex mx;

void foo() {
    while(1) {
        lock_guard<mutex> locker(mx);
        i++;
        if(i == 5000) {
            break;
        }
    }
}

void boo() {
    while(1) {
        if(i == 100) {
            lock_guard<mutex> locker(mx);
            i = 5000;
            break;
         }
    }
}

int main(int argc, char *argv[])
{
    thread th1(foo);
    thread th2(boo);

    th1.join();
    th2.join();

    return 0;
}

为什么我会有这样的结果? 如何更改代码以使其正确? 你能告诉我你的想法吗? 谢谢。

即使boo先开始运行,它也可能永远不会看到i==100

如果您只有一个 CPU,那么当i==100时,CPU 不太可能从foo切换到boo

如果你有多个 CPU,那么i==100可能永远不会进入foo's缓存,因为i不是易失性的,并且互斥锁在读取之间没有锁定。

实际上,编译器在第一次之后甚至不必读取i ,因为没有 memory 障碍。 它可以假设值没有改变。

即使你要解决这个问题,在boo注意到之前i可能会增加超过 100 的明显可能性仍然存在。 看起来您希望这两个线程“轮流”,但事实并非如此。

您的解决方案存在一些并发问题:

  1. 您必须始终如一地锁定互斥体。 所有i的访问都必须受到互斥量的保护,因此在if (i == 100) {行也是如此。 在没有同步的情况下,编译器可以自由地优化线程,就好像它在孤立地运行一样,并假设i永远不会改变。

  2. 不能保证boo会在foo之前开始。 如果它在之后开始, i将已经增加到远高于100

  3. 不能保证互斥锁定是公平的。 竞争同一个互斥量的两个线程不会以交错方式运行。 这意味着foo可能会在boo有机会运行之前多次增加i ,因此boo看到的i的值可能很容易从0跳到1000 ,跳过所需的100

  4. 孤立地, foo将“逃跑”,将i增加到远远超过5000 应该有一些退出或重新启动条件。

如何更改代码以使其正确?

添加一些同步以强制交错处理。 例如,使用condition_variable s 在线程之间发出信号:

int i = 0;

mutex mx;
condition_variable updated_cond;
bool updated = false;
condition_variable consumed_cond;
bool consumed = true;

void foo() {
    while (1) {
        unique_lock<mutex> locker(mx);
        consumed_cond.wait(locker, [] { return consumed; });
        consumed = false;
        if (i == 5000) {
            break;
        }
        std::cout << "foo: i = " << i << "+1\n";
        i++;
        updated = true;
        updated_cond.notify_one();
    }
    std::cout << "foo exiting\n";
}

void boo() {
    for (bool exit = false; !exit; ) {
        unique_lock<mutex> locker(mx);
        updated_cond.wait(locker, [] { return updated; });
        updated = false;
        std::cout << "boo: i = " << i << "\n";
        if (i == 100) {
            i = 5000;
            exit = true;
        }
        consumed = true;
        consumed_cond.notify_one();
    }
    std::cout << "boo exiting\n";
}

该程序的行为是未定义的,因此对其所做的推理是徒劳的。 问题是boo读取i的值, foo都读取和写入i的值,但是booif (i == 100)i的读取相对于发生在foo中的写入是无序的。 那是一场数据竞赛,程序的行为是未定义的。 当然,您可以猜测可能会发生什么,但如果您希望代码正确运行,则必须确保不存在数据竞争。 这意味着使用某种形式的同步:要么将boo中的锁移到if之前,要么摆脱互斥并将i的类型更改为std::atomic<int>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM