线程安全的方式来建立互斥体保护成C ++类?(Thread-safe way to build mu

2019-07-30 14:19发布

我想实现的一个项目我工作在C ++生产者/消费者模型的多线程程序。 其基本思路是,主线程创建第二个线程,以观看新数据,处理数据的串行端口,并把结果在定期由主线程轮询的缓冲区。 我以前从来没有写多线程程序。 我一直在阅读大量教程,但他们都在C.我觉得我有一个手柄上的基本概念,但我想C ++ IFY它。 对于缓冲,我要创建具有内置的互斥保护数据类。这是我想出了。

1)我要对这个错误的方式? 有没有办法实现保护数据类的明智之选?

2)将在下面的代码发生什么,如果两个线程试图调用ProtectedBuffer::add_back()在同一时间?

#include <deque>
#include "pthread.h"

template <class T>
class ProtectedBuffer {
  std::deque<T> buffer;
  pthread_mutex_t mutex;
public:
  void add_back(T data) {
    pthread_mutex_lock(&mutex);
    buffer.push_back(data);
    pthread_mutex_unlock(&mutex);
  }
  void get_front(T &data) {
    pthread_mutex_lock(&mutex);
    data = buffer.front();
    buffer.pop_front();
    pthread_mutex_unlock(&mutex);
  }
};

编辑:感谢所有伟大的建议。 我试过下面来实现它们。 我还添加了一些错误检查,所以如果一个线程以某种方式设法试图锁定相同的互斥两次会优雅地失败。 我认为。

#include "pthread.h"
#include <deque>


class Lock {
    pthread_mutex_t &m;
    bool locked;
    int error;
public:
    explicit Lock(pthread_mutex_t & _m) : m(_m) {
        error = pthread_mutex_lock(&m);
        if (error == 0) {
            locked = true;
        } else {
            locked = false;
        }
    }
    ~Lock() {
        if (locked)
            pthread_mutex_unlock(&m);
    }
    bool is_locked() {
        return locked;
    }
};

class TryToLock {
    pthread_mutex_t &m;
    bool locked;
    int error;
public:
    explicit TryToLock(pthread_mutex_t & _m) : m(_m) {
        error = pthread_mutex_trylock(&m);
        if (error == 0) {
            locked = true;
        } else {
            locked = false;
        }
    }
    ~TryToLock() {
        if (locked)
            pthread_mutex_unlock(&m);
    }
    bool is_locked() {
        return locked;
    }
};

template <class T>
class ProtectedBuffer{
    pthread_mutex_t mutex;
    pthread_mutexattr_t mattr;
    std::deque<T> buffer;
    bool failbit;

    ProtectedBuffer(const ProtectedBuffer& x);
    ProtectedBuffer& operator= (const ProtectedBuffer& x);
public:
    ProtectedBuffer() {
        pthread_mutexattr_init(&mattr);
        pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ERRORCHECK);
        pthread_mutex_init(&mutex, &mattr);
        failbit = false;
    }
    ~ProtectedBuffer() {
        pthread_mutex_destroy(&mutex);
        pthread_mutexattr_destroy(&mattr);
    }
    void add_back(T &data) {
        Lock lck(mutex);
        if (!lck.locked()) {
            failbit = true;
            return;
        }
        buffer.push_back(data);
        failbit = false;
    }
    void get_front(T &data) {
        Lock lck(mutex);
        if (!lck.locked()) {
            failbit = true;
            return;
        }
        if (buffer.empty()) {
            failbit = true;
            return;
        }
        data = buffer.front();
        buffer.pop_front();
        failbit = false;
    }
    void try_get_front(T &data) {
        TryToLock lck(mutex);
        if (!lck.locked()) {
            failbit = true;
            return;
        }
        if (buffer.empty()) {
            failbit = true;
            return;
        }
        data = buffer.front();
        buffer.pop_front();
        failbit = false;
    }
    void try_add_back(T &data) {
        TryToLock lck(mutex);
        if (!lck.locked()) {
            failbit = true;
            return;
        }
        buffer.push_back(data);
        failbit = false;
    }
};

Answer 1:

几件事情:

  • 您需要初始化mutexpthread_mutex_init在构造与释放它pthread_mutex_destroy在析构函数。

  • 你必须让你的类不可复制和不可转让(或以其他方式正确执行的拷贝构造函数和赋值操作符;见上文)。

  • 这是值得作出SBRM辅助类的锁:

     class Lock { pthread_mutex_t & m; public: explicit Lock(pthread_mutex_t & _m) : m(_m) { pthread_mutex_lock(&m); } ~Lock() { pthread_mutex_unlock(&m); } }; 

    现在您可以像同步范围{ Lock lk(mutex); /* ... */ } { Lock lk(mutex); /* ... */ }

至于问题2:并发访问被锁定的互斥的方式序列化。 一个竞争的线程将睡在收购互斥锁。



Answer 2:

我要对这个错误的方式? 有没有办法实现保护数据类的明智之选?

对于你执行,我认为你有一个良好的开端。 既然你问有关C ++ ifying,然后如果你有一个支持C ++ 11编译器,你可以使用新的线程支持。

你刚才提到你想要的主线程来查询该缓冲区,但我没有看到,将允许它这样做的任何机构。 任一get_front当存在没有在缓冲器应提供一个错误,或get_buffer直到数据可用应该阻塞调用者。

#include <deque>
#include <mutex>
#include <condition_variable>
#include <stdexcept>

template <class T>
class ProtectedBuffer {
  std::deque<T> buffer;
  std::mutex mtx;
  std::condition_variable empty_cnd;
  void get_front_i(T &data) {
    data = buffer.front();
    buffer.pop_front();
  }
public:
  void add_back(T data) {
    std::lock_guard<std::mutex> g(mtx);
    bool was_empty = buffer.empty();
    buffer.push_back(data);
    if (was_empty) empty_cnd.notify_one();
  }
  void get_front_check(T &data) {
    std::lock_guard<std::mutex> g(mtx);
    if (buffer.empty()) throw std::underflow_error("no data");
    get_front_i(data);
  }
  void get_front_block(T &data) {
    std::lock_guard<std::mutex> g(mtx);
    std::unique_lock<std::mutex> u(mtx);
    while (buffer.empty()) empty_cnd.wait(u);
    get_front_i(data);
    if (!buffer.empty()) empty_cnd.notify_one();
  }
};

如果你想绑定你有多少数据添加到您的缓冲区,你可以添加一个类似full_cnd条件变量来检查充分条件在其上add_back通话将等待,如果它是真实的。 然后, get_front_i当缓冲区未盛满了方法可能预示。

将下面的代码发生什么,如果两个线程试图在同一时间打电话ProtectedBuffer :: add_back()?

由于add_back从互斥保护的,如果两个线程调用它在同一时间,一个线程将从呼叫阻塞push_back直到其他线程完成。



Answer 3:

您具备了基础知识,但我会采取这一步,包裹在自己的RAII包装,例如互斥体本身:

#include <deque> 
#include "pthread.h" 

class ProtectedMutex
{
  pthread_mutex_t &mutex; 
public:
  ProtectedMutex(pthread_mutex_t &m)
    : mutex(m); 
  {
    pthread_mutex_lock(&mutex); 
  }
  ~ProtectedMutex()
  {
    pthread_mutex_unlock(&mutex); 
  }
};

template <class T> 
class ProtectedBuffer { 
  std::deque<T> buffer; 
  pthread_mutex_t mutex; 
public: 
  void add_back(T data) { 
    ProtectedMutex m(mutex); 
    buffer.push_back(data); 
  } 
  void get_front(T &data) { 
    ProtectedMutex m(mutex); 
    data = buffer.front(); 
    buffer.pop_front(); 
  } 
}; 


Answer 4:

“把结果在定期由主线程轮询的缓冲” - CPU浪费和延迟。

“我要对这个错误的方式?” - 是的。 我不知道你有你的系统辅助线程<> GUI线程通讯科什么样的支持,但总是还有的PostMessage的()API。

你需要一个缓冲级,当然,与串列Rx数据的数据成员,做协议/“处理数据”的方法。 你并不需要太多的东西。 在你的第二个线程,创建缓冲区类的实例。 加载它,处理数据和PostMessage的/调度/的BeginInvoke其指向您的GUI线程。 在串行线程处理代码的最下一行,在同一个实例的指针VAR将数据从串口下一负载创建另一个实例。 之后显示/记录/无论在GUI,GUI线程应该删除()的*缓冲区收到。

无延迟,无CPU浪费,没有数据复制,没有串口线的机会和GUI线程曾工作于同一缓冲区的实例,没有讨厌的,复杂的缓冲共享代码,没有锁,没有麻烦。 它只是将工作得很好。

还有什么将是混乱的。

编辑 - 忘了(2) - 不知道,。 不会用驳船极触摸它..



文章来源: Thread-safe way to build mutex protection into a C++ class?