【问题标题】:Suspend pthreads without using condition在不使用条件的情况下挂起 pthread
【发布时间】:2010-06-29 13:05:52
【问题描述】:

我想暂停 pthreads,但显然没有 pthread_suspend 之类的功能。我在某处阅读了有关使用互斥锁和条件暂停 pthread 的信息,并将其用作以下内容:

#include <pthread.h>

class PThread {
public:
pthread_t myPthread;
pthread_mutex_t m_SuspendMutex;
pthread_cond_t m_ResumeCond;

void start() {
pthread_create(&myPthread, NULL, threadRun, (void*)this );
}

Thread() { }

void suspendMe() {
pthread_cond_wait(&m_ResumeCond,&m_SuspendMutex);
}

void resume() {
pthread_cond_signal(&m_ResumeCond);
}
};

但我不明白为什么我们需要互斥锁和条件来挂起和恢复 pthread。不使用条件是否可以暂停和恢复?

【问题讨论】:

    标签: c++ linux pthreads


    【解决方案1】:

    您的代码不正确 - pthread_cond_wait() 要求在您调用互斥锁时已将其锁定:

    void suspendMe()
    {
        pthread_mutex_lock(&m_SuspendMutex);
        pthread_cond_wait(&m_ResumeCond, &m_SuspendMutex);
        pthread_mutex_unlock(&m_SuspendMutex);
    }
    

    但是,这仍然是错误的。线程可以以任何名称从pthread_cond_wait() 唤醒,不一定只有在收到信号时才必须。这意味着您需要将pthread_cond_wait() 与一些共享状态配对,该状态编码线程真正等待的条件 - 在最简单的情况下,您可以只使用标志变量。 pthread_cond_signal() 用于告诉线程它应该唤醒并重新检查共享状态。将此应用到您的实现中:

    class PThread {
        public:
    
        pthread_t myPthread;
        bool suspended;
        pthread_mutex_t m_SuspendMutex;
        pthread_cond_t m_ResumeCond;
    
        void start() {
            suspended = false;
            pthread_create(&myPthread, NULL, threadRun, (void*)this );
        }
    
        Thread() { }
    
        void suspendMe() {
            pthread_mutex_lock(&m_SuspendMutex);
            suspended = true;
            do {
                pthread_cond_wait(&m_ResumeCond, &m_SuspendMutex);
            } while (suspended);
            pthread_mutex_unlock(&m_SuspendMutex);
        }
    
        void resume() {
            /* The shared state 'suspended' must be updated with the mutex held. */
            pthread_mutex_lock(&m_SuspendMutex);
            suspended = false;
            pthread_cond_signal(&m_ResumeCond);
            pthread_mutex_unlock(&m_SuspendMutex);
        }
    };
    

    提供互斥锁的原因是为了保护共享状态并避免竞争条件 - pthread_cond_wait() 函数实际上在等待时执行原子解锁并等待,这可以避免“错过唤醒”。例如,在此代码中,互斥锁可防止 suspendedsuspended = true;pthread_cond_wait() 行之间更改为 false。

    【讨论】:

    • 另外,你需要在唤醒后检查你的状态,因为系统可能唤醒你的线程,条件为假!例如,系统可能已经唤醒了 10 个线程,其中一个已经完成了工作。
    • @Zan Lynx:是的,POSIX 在这一点上非常清楚:“可能会发生来自 pthread_cond_timedwait() 或 pthread_cond_wait() 函数的虚假唤醒。”(尽管我相信Linux 实现不会进行虚假唤醒)。
    • 所以pthread_cond_wait 就像一个非常松散的狗链。你的比特犬随时都可能开始咬邻居。
    • 不,它更像是商店柜台上的铃铛。如果你按铃,店主肯定会来柜台问你要什么,但如果你不按,他们可能会来。
    【解决方案2】:

    如果一个线程没有在某些条件下等待,你怎么能“发出信号”让它恢复。它不能只是停止执行任何事情然后神奇地重新开始,所以它会等待一个条件。

    详细地说,在 pthreads 中,恢复线程的方法实际上是使用条件变量。没有可用于以任何其他方式暂停/恢复线程的 API。等待pthread_cond_wait 很便宜,它会阻塞直到发出条件信号,而不使用(很多?)CPU。您使用条件来通知线程唤醒,并且需要互斥锁来保护唤醒时对条件变量和线程中代码的访问。

    【讨论】:

    • "你怎么能“发出信号”让它恢复?"在其他 API 中是可能的!例如,Windows API 中的ResumeThread 通过接受整数线程 ID 来工作。还有一个SuspendThread 函数。当工作可用或完成时,您可以一起轻松地唤醒或挂起线程池中的工作线程(从主线程)。
    • There is a nice paragraph here 描述了同样的事情。
    • @bobobobo 您是否阅读了这些功能的文档?它们不是用于线程同步,而是用于调试器。 pthreads 是一个提供线程功能的库,包括同步。它不是用于实现调试器的库,因此它没有设计用于调试器的等效函数
    • 来自文档:“这个函数主要是为调试器设计的。它不打算用于线程同步。在拥有同步对象的线程上调用 SuspendThread,例如互斥锁或如果调用线程试图获取挂起线程拥有的同步对象,则可能导致死锁。为避免这种情况,应用程序中的非调试器线程应向其他线程发出信号以挂起自身。目标线程必须设计为监视此信号并做出适当的响应。”
    • 其实是的,我昨天刚做了,我意识到我错了。我决定对此事保持沉默,但你指出了这一点,这很好。
    【解决方案3】:

    条件总是与互斥锁相关联。通常,线程会休眠,因为它正在等待状态更改以指示它有工作要做。您需要互斥锁来保护对该状态的访问,以及发出更改信号的条件。

    唤醒一个线程而不告诉它你为什么唤醒它是一件很奇怪的事情,所以没有特殊的方法可以做到;唯一的方法是使用正常机制,但没有共享状态。

    如果出于某种原因你想暂停和恢复另一个线程的线程,独立于让它工作,那么你可以使用pthread_kill 发送SIGSTOPSIGCONT 信号;我从来没有尝试过这样做,所以我不知道它是否受支持。

    【讨论】:

      【解决方案4】:

      互斥锁用于确保独占访问,而条件变量用于根据事件同步线程。

      我们需要互斥锁来确保条件变量不会无限等待。 要记住的一件事是锁定和解锁的互斥操作保证是原子的,但条件变量不必是原子的。即当条件变量等待中途时,线程可以被调度。

      考虑以下没有 Mutex 作为条件变量的情况。

      线程 1


      1)执行一些操作
      2)等待条件变量
      3)继续操作

      线程 2


      1) 执行一些操作
      2) 给条件变量发信号
      3)继续操作

      在线程 1 中,步骤 2 不能保证是原子的。如果线程 1 在完成 step1 之前被调度程序推出 RUNNING 状态。 现在线程 2 开始执行并向条件变量发出信号。当线程 1 恢复执行时,它将完成剩余的低级指令并开始等待。 线程 1 以无限等待结束,因为条件变量的信号甚至在等待之前就出现了。

      所以正确的使用方法是(我相信问题中提到的代码没有达到预期的效果)

      线程 1:-


      1)工作到一定条件必须发生的地步(比如“count”必须达到指定值)
      2)锁定关联互斥体
      3) 调用 pthread_cond_wait() 以执行来自 Thread1 的阻塞等待信号。 (请注意,对 pthread_cond_wait() 的调用会自动以原子方式解锁关联的互斥变量,以便 Thread2 可以使用它)
      4) 收到信号后,醒来。互斥锁是自动和原子锁定的。
      5)显式解锁互斥锁

      线程2


      1) 工作
      2)锁定关联互斥体
      3) 更改 Thread1 正在等待的全局变量的值。
      4)检查全局 Thread1 等待变量的值。如果它满足所需条件,则发出 Thread1 信号。
      5)解锁互斥锁。 继续

      【讨论】:

        【解决方案5】:

        看起来没有任何 Linux 替代 Windows API SuspendThread 函数。如果不在该线程过程中注入任何代码,就不可能暂停 Linux 线程。

        【讨论】:

        • SuspendThread 在用户模式代码中总是很臭。甚至文档都说“这个函数主要是为调试器设计的。它不打算用于线程同步。”,这对于任何想要使用它的人来说应该足够了。
        【解决方案6】:

        更重要的是 - 你最终想要做什么? - 我怀疑答案不是“暂停线程”。可能是你的程序设计有问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-03
          • 1970-01-01
          • 1970-01-01
          • 2011-01-04
          相关资源
          最近更新 更多