锁定步进pthread互斥锁

时间:2018-08-06 09:21:08

标签: c++ c multithreading pthreads

我不知道这是否是一个好习惯,但是我正在处理输入数据的实时流,并以锁步的方式使用pthread来一次允许一个线程同时执行不同的操作。这是每个线程的程序流:

void * my_thread() {
    pthread_mutex_lock(&read_mutex);

    /*
    read data from a stream such as stdin into global buffer
    */

    pthread_mutex_lock(&operation_mutex);
    pthread_mutex_unlock(&read_mutex);

    /*
     perform some work on the data you read 
    */

   pthread_mutex_lock(&output_mutex);
   pthread_mutex_unlock(&operation_mutex);

   /*
    Write the data to output such as stdout
    */
   pthread_mutex_unlock(&output_mutex);
}

我知道有pthread条件锁,但是我的方法是好主意还是坏主意?我在各种大小的流上对此进行了测试,我试图考虑一些极端情况,以使这种僵局,产生竞争状况或同时发生这两种情况。我知道互斥锁不能保证线程顺序的执行,但是我需要帮助来考虑可能会破坏这种情况的情况。

更新:

我放弃了这个,但是最近有一段时间重新考虑这个。我使用C ++线程和互斥体重写了代码。我正在尝试使用条件变量,但没有这种运气。这是我解决问题的方法:

void my_thread_v2() {
    //Let only 1 thread read in at a time
    std::unique_lock<std::mutex> stdin_lock(stdin_mutex);
    stdin_cond.wait(stdin_lock);

    /*
    Read from stdin stream
    */

    //Unlock the stdin mutex
    stdin_lock.unlock();
    stdin_cond.notify_one();

    //Lock step
    std::unique_lock<std::mutex> operation_lock(operation_mutex);
    operation_cond.wait(operation_lock);

    /*
     Perform work on the data that you read in
     */

    operation_lock.unlock();
    operation_cond.notify_one();

    std::unique_lock<std::mutex> stdout_lock(stdout_mutex);
    stdout_cond.wait(stdout_lock);

    /*
     Write the data out to stdout
     */

    //Unlock the stdout mutex
    stdout_lock.unlock();
    stdout_cond.notify_one();
}

我知道这段代码的问题是,没有办法表明第一个条件。我绝对不理解条件变量的正确使用。我看了有关cpp引用的各种示例,但似乎无法摆脱这种想法,即最初的方法也许是我要做的唯一方法是锁定线程。有人可以阐明这一点吗?

更新2:

因此,我实现了一个简单的Monitor类,该类利用C ++ condition_variableunique_lock

class ThreadMonitor{
public:
    ThreadMonitor() : is_occupied(false) {}
    void Wait() {
        std::unique_lock<std::mutex> lock(mx);
        while(is_occupied) {
            cond.wait(lock);
        }
        is_occupied = true;
    }

    void Notify() {
        std::unique_lock<std::mutex> lock(mx);
        is_occupied = false;
        cond.notify_one();
    }

private:
    bool is_occupied;
    std::condition_variable cond;
    std::mutex mx;
};

这是我的初始方法,假设我有三个分别称为ThreadMonitorstdin_monoperation_mon的{​​{1}}:

stdout_mon

问题是数据仍然被破坏,所以我不得不回到锁步的原始逻辑:

void my_thread_v3() {
    //Let only 1 thread read in at a time
    stdin_mon.Wait();

    /*
    Read from stdin stream
    */

    stdin_mon.Notify();

    operation_mon.Wait();

    /*
     Perform work on the data that you read in
     */

    operation_mon.Notify();

    stdout_mon.Wait();
    /*
     Write the data out to stdout
     */

    //Unlock the stdout
    stdout_mon.notify();
}

我开始怀疑,如果线程顺序很重要,这是处理它的唯一方法。我还质疑使用监视器void my_thread_v4() { //Let only 1 thread read in at a time stdin_mon.Wait(); /* Read from stdin stream */ operation_mon.Wait(); stdin_mon.Notify(); /* Perform work on the data that you read in */ stdout_mon.Wait(); operation_mon.Notify(); /* Write the data out to stdout */ //Unlock the stdout stdout_mon.notify(); } 而不是仅使用互斥锁的好处是什么。

1 个答案:

答案 0 :(得分:0)

您的方法存在的问题是,当另一个线程正在读取数据时,您仍然可以修改数据:

  1. 线程A获得了读取,然后进行操作并再次释放读取,然后开始写入 some 数据,但是被中断了。
  2. 现在线程B运行,获取读取并可以读取部分修改的,可能不一致的数据!

我假设您想允许多个线程读取同一数据而不会阻塞,但是一旦写入,数据就应受到保护。最后,在输出数据时,我们只是再次读取修改后的数据,因此可以再次并发执行此操作,但是需要防止同时写入。

与其拥有多个互斥锁实例,不如使用读/写互斥锁来做得更好:

  1. 任何仅读取数据的函数都会获得读取锁。
  2. 任何打算进行写操作的函数都从一开始就获取写锁定(请注意,首先获取读,然后写锁定而不在之间释放读锁定 可能会导致死锁; 如果释放了它们之间的读取锁定,那么您的数据处理就必须健壮,以防止数据在 之间被另一个线程修改!)。 / li>
  3. 将写锁减少为共享锁而又不释放它们是安全的,因此我们现在可以在输出之前这样做。如果在写数据和输出数据之间不能修改数据,我们甚至需要而不必完全释放锁。

最后一点是有问题的,因为C ++标准的线程支持库和pthreads库都不支持。

对于C ++ Boost,它提供了solution;如果您不想或不能(C!)使用boost,那么一种简单但可能不是最有效的方法就是保护通过另一个互斥锁获取写锁定:

  1. 获取标准(非rw)互斥体以保护读写互斥体
  2. 获取用于编写的RW互斥锁
  3. 释放保护互斥锁
  4. 读取数据,写入修改的数据
  5. 获取保护互斥锁
  6. 释放RW互斥锁
  7. 重新获取RW互斥锁以进行读取;是否也需要获取另一个线程来读取也没关系,我们只需要在此处防止写锁定就可以锁定
  8. 释放保护互斥锁
  9. 输出
  10. 释放RW互斥锁(无需保护)...

非修改功能只能获取读锁而没有任何进一步的保护,与...没有任何冲突。

在C ++中,您更喜欢使用thread support library并另外免费获取与平台无关的代码,在C中,您将像以前一样使用标准的pthread互斥锁来保护获取写锁,并使用pthread的RW变体用于读写锁定。