代码之家  ›  专栏  ›  技术社区  ›  Sid Datta

未锁定pthread_cond_timedwait和pthread_cond_signal的互斥体(在Linux上)

  •  6
  • Sid Datta  · 技术社区  · 17 年前

    调用pthread_cond_timedwait而不首先对关联的互斥体进行锁,并且在调用pthread_cond_signal时也不进行互斥体锁,这有什么缺点吗?

    在我的例子中,真的没有需要检查的条件,我想要一个非常类似于Java wait(long)和notify()的行为。

    6 回复  |  直到 17 年前
        1
  •  12
  •   Evan Teran    17 年前

    这 pthread_cond_timedwait() 和 pthread_cond_wait() 功能应 阻塞条件变量。他们 调用线程或未定义 行为结果。

    http://opengroup.org/onlinepubs/009695399/functions/pthread_cond_timedwait.html

    原因是实现可能希望依赖于锁定互斥体,以便安全地将您添加到服务员列表中。它可能希望释放互斥体,而不首先检查它是否被持有。

    如果可预测的调度行为是 必需的,则该互斥体被锁定 线程调用 pthread_cond_signal() 或 pthread_cond_broadcast() .

    http://www.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_signal.html

    在我的脑海中,我不确定如果你在没有锁的情况下发出信号,具体的比赛条件是什么,会扰乱调度器的行为。因此,我不知道未定义的调度器行为会变得有多糟糕:例如,在广播中,服务员可能无法按优先级顺序获得锁(或者按照您的特定调度器的正常行为)。或者服务员可能会“迷路”。

    不过,一般来说,对于条件变量,您希望设置条件(至少是一个标志)和信号,而不仅仅是信号,为此您需要使用互斥量。原因是,否则,如果你与另一个调用wait()的线程并发,那么你会根据等待()或信号()是否获胜而得到完全不同的行为:如果信号()先潜入,那么即使你关心的信号已经发生,你也会等待完全超时。这很少是条件变量用户想要的,但对你来说可能没问题。也许这就是文档中所说的“不可预测的调度器行为”的意思——突然之间,时间片对程序的行为变得至关重要。

    顺便说一句,在Java中,你必须有锁才能notify()或notifyAll():

    此方法只能由以下对象调用 该线程的所有者 对象的监视器。

    http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html#notify()

    Java synchronized{/}/wait/notifty/notifyAll行为类似于pthread_mutex_lock/pthread_mutex-unlock/pthread_cond_wait/phtread_cond_signal/pthread_cond_broadcast,这并非巧合。

        2
  •  10
  •   TheJuice    15 年前

    Butenhof出色的“使用POSIX线程编程”在第3.3.3章末尾讨论了这一点。

    基本上,在不锁定互斥体的情况下发送condvar信号是一种 潜在的

        3
  •  3
  •   Nikolai Fetissov    17 年前

    等待与互斥体配对的条件变量的目的是 原子地 输入wait并释放锁,即允许其他线程修改受保护的状态,然后再次原子性地接收状态更改的通知并获取锁。你所描述的可以用许多其他方法来完成,比如管道、插座、信号,或者——可能是最合适的方法- 信号量 .

        4
  •  1
  •   Evan Teran    17 年前

    我认为这应该奏效(注意未经测试的代码):

    // initialize a semaphore
    sem_t sem;
    sem_init(&sem,
        0, // not shared
        0  // initial value of 0
        );
    
    
    // thread A
    struct timespec tm;
    struct timeb    tp;
    
    const long sec      = msecs / 1000;
    const long millisec = msecs % 1000;
    
    ftime(&tp);
    tp.time += sec;
    tp.millitm += millisec;
    if(tp.millitm > 999) {
        tp.millitm -= 1000;
        tp.time++;
    }
    tm.tv_sec  = tp.time;
    tm.tv_nsec = tp.millitm * 1000000;
    
    // wait until timeout or woken up
    errno = 0;
    while((sem_timedwait(&sem, &tm)) == -1 && errno == EINTR) {
        continue;
    }
    
    return errno == ETIMEDOUT; // returns true if a timeout occured
    
    
    // thread B
    sem_post(&sem); // wake up Thread A early
    
        5
  •  0
  •   Kaz    13 年前

    互斥体的目的是保护对程序中某些共享变量的访问,使其行为原子化。当在互斥体内执行信号操作时,它会导致数百个无关的机器周期包含在互斥体中,而这些机器周期与保护共享数据无关。它可能从用户空间一直调用到内核。

    标准中关于“可预测的调度器行为”的注释完全是伪造的。

    当我们希望机器以可预测的、定义良好的顺序执行语句时,实现这一点的工具是在单个执行线程内对语句进行排序: S1 ; S2 .声明 S1 在“预定”之前 S2 .

    当我们意识到某些操作是独立的,它们的调度顺序并不重要,并且可以实现性能优势时,我们使用线程,比如更及时地响应实时事件或在多个处理器上进行计算。

    优先级 优先级解决了当N个语句中的任何一个可能被调度执行时首先发生的事情。在多线程下排序的另一个工具是排队。事件由一个或多个线程放入队列,单个服务线程按队列顺序处理事件。

    底线是,放置 pthread_cond_broadcast 不是控制执行顺序的合适工具。它不会使执行顺序变得可预测,因为程序在每个平台上突然具有完全相同的、可重复的行为。

        6
  •  -1
  •   nos    17 年前

    执行也没有。它可以按预期工作。它可能会崩溃你的应用程序。它可以正常工作多年,然后比赛条件会让你的应用程序变得一团糟。它可能会陷入僵局。

    基本上,如果任何文档建议发生任何未定义/不可预测的事情,除非你按照文档告诉你的去做,否则你最好这样做。否则,事情可能会在你面前爆炸。