【问题标题】:Odd thread behaviors奇怪的线程行为
【发布时间】:2018-03-06 00:02:35
【问题描述】:

下一个代码通常会打印 BA,但有时它可以打印 BBAA、BAAB、... 怎么可能得到两个 A 或 B?!但是,此代码从不打印三个 A 或 B。这两个函数(生产和消费)都运行大量线程。非常感谢。

int permission;
void set_permission(int v) {
    permission = v;
    printf("%c", v + 'A');fflush(stdin);
}
void* produce(void*) {
    for (;;) {
        pthread_mutex_lock(&mr1);
        set_permission(1);
        while (permission == 1);
        pthread_mutex_unlock(&mr1);
    }
}
void* consume(void*) {
    for (;;) {
        pthread_mutex_lock(&mr2);
        while (permission == 0);
        set_permission(0);
        pthread_mutex_unlock(&mr2);
    }
}

【问题讨论】:

  • fflush(stdin); 导致未定义的行为;不要这样做
  • 我看到了 2 个不相关的互斥锁。看起来您可以删除同步程序并获得相同的结果。
  • 对两个不同互斥体的引用是什么?如果代码试图保护变量permission,那么所有互斥锁引用都应该指向同一个互斥锁。建议:for( ;; ) { while( !permission ); pthread_mutex_lock( &mr1 ); set_permission( 1 ); pthread_mutex_unlock( &mr1 ); } 消费者线程看起来非常相似并且会使用相同的互斥锁
  • @ArtemyVysotsky,目的(虽然实现不正确)是避免变量访问的竞争条件:permissions
  • 通常,此类问题的答案是“您的代码具有未定义的行为,因此绝对有可能发生任何事情”。真正的问题是导致 UB 的原因以及如何解决它。

标签: c++ c multithreading pthreads mutex


【解决方案1】:

您的线程未同步,因为它们没有使用相同的互斥锁。

另一个线程只能设法将permission 设置为1 或0,但还不能产生输出。在这种情况下,第一个线程似乎运行了整整两轮。

当内存内容在内核之间同步并且两个线程都写入时,相应线程的写入也可能完全丢失。互斥体还可以防止这种情况发生,因为它建立了严格的内存访问顺序,简单地说,它保证了在一个互斥体保护下发生的一切对同一个互斥体的下一个用户完全可见。

打印同一个字符 3 次或更多次的可能性很小,因为其间最多发生一次写入,因此最多一次丢失写入,或一次输出乱序。但这并不能保证。

如果您在一个根本没有隐式内存同步的系统上工作,您的代码也可能直接解决死锁,因为在一个互斥锁下完成的写入永远不会传播给另一个互斥锁的用户。 (实际上并没有发生,因为 IO 操作仍然引入了一些同步。)

【讨论】:

  • 我将 printf 放在了赋值之前,这消除了所有双字符!谢谢一百万!
  • @vollitwr 只要您不使用相同的互斥锁,这仍然只是消除了无序输出。您的代码仍然容易丢失写入,除非permissionatomic 或者您使用的是相同的互斥锁。这个错误更难引发,但它仍然可能发生。
猜你喜欢
  • 1970-01-01
  • 2015-01-27
  • 1970-01-01
  • 1970-01-01
  • 2015-01-07
  • 2018-09-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多