将互斥保护构建到C++;上课? 我正在尝试为我正在进行的项目在C++中实现一个生产者/消费者模型多线程程序。其基本思想是,主线程创建第二个线程来监视串行端口中的新数据,处理数据,并将结果放入由主线程定期轮询的缓冲区中。我以前从未编写过多线程程序。我已经阅读了很多教程,但都是C语言的。我想我已经掌握了基本概念,但我正在尝试用C++来实现它。对于缓冲区,我想创建一个内置互斥保护的数据类。这就是我想到的

将互斥保护构建到C++;上课? 我正在尝试为我正在进行的项目在C++中实现一个生产者/消费者模型多线程程序。其基本思想是,主线程创建第二个线程来监视串行端口中的新数据,处理数据,并将结果放入由主线程定期轮询的缓冲区中。我以前从未编写过多线程程序。我已经阅读了很多教程,但都是C语言的。我想我已经掌握了基本概念,但我正在尝试用C++来实现它。对于缓冲区,我想创建一个内置互斥保护的数据类。这就是我想到的,c++,multithreading,mutex,C++,Multithreading,Mutex,1) 我走错方向了吗?有没有更智能的方法来实现受保护的数据类 2) 如果两个线程试图同时调用ProtectedBuffer::add_back(),那么下面的代码会发生什么情况 #include <deque> #include "pthread.h" template <class T> class ProtectedBuffer { std::deque<T> buffer; pthread_mutex_t mutex; public: vo

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);
  }
};
#包括
#包括“pthread.h”
模板
类ProtectedBuffer{
std::deque缓冲区;
pthread_mutex_t mutex;
公众:
void add_back(T数据){
pthread_mutex_lock(&mutex);
缓冲区。推回(数据);
pthread_mutex_unlock(&mutex);
}
无效获取前端(T和数据){
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;
    }
};
#包括“pthread.h”
#包括
类锁{
pthread_mutex_t&m;
布尔锁定;
整数误差;
公众:
显式锁(pthread\u mutex\u t&\u m):m(\u m){
错误=pthread\u mutex\u lock(&m);
如果(错误==0){
锁定=真;
}否则{
锁定=错误;
}
}
~Lock(){
如果(已锁定)
pthread_mutex_unlock(&m);
}
布尔已锁定(){
返回锁定;
}
};
类TryToLock{
pthread_mutex_t&m;
布尔锁定;
整数误差;
公众:
显式TryToLock(pthread\u mutex\u t&\u m):m(\u m){
错误=pthread\u mutex\u trylock(&m);
如果(错误==0){
锁定=真;
}否则{
锁定=错误;
}
}
~TryToLock(){
如果(已锁定)
pthread_mutex_unlock(&m);
}
布尔已锁定(){
返回锁定;
}
};
模板
类ProtectedBuffer{
pthread_mutex_t mutex;
pthread_mutextatr_t mattr;
std::deque缓冲区;
布尔故障位;
ProtectedBuffer(常量ProtectedBuffer&x);
ProtectedBuffer和运算符=(const ProtectedBuffer和x);
公众:
ProtectedBuffer(){
pthread_mutexattr_init(&mattr);
pthread_mutextatr_settype(&mattr,pthread_MUTEX_ERRORCHECK);
pthread_mutex_init(&mutex,&mattr);
failbit=false;
}
~ProtectedBuffer(){
pthread_mutex_destroy(&mutex);
pthread_mutexattr_destroy(&mattr);
}
无效添加回(T&data){
锁定lck(互斥);
如果(!lck.locked()){
failbit=true;
返回;
}
缓冲区。推回(数据);
failbit=false;
}
无效获取前端(T和数据){
锁定lck(互斥);
如果(!lck.locked()){
failbit=true;
返回;
}
if(buffer.empty()){
failbit=true;
返回;
}
data=buffer.front();
buffer.pop_front();
failbit=false;
}
无效尝试获取前端(T&data){
TryToLock lck(互斥锁);
如果(!lck.locked()){
failbit=true;
返回;
}
if(buffer.empty()){
failbit=true;
返回;
}
data=buffer.front();
buffer.pop_front();
failbit=false;
}
无效尝试添加回(T&data){
TryToLock lck(互斥锁);
如果(!lck.locked()){
failbit=true;
返回;
}
缓冲区。推回(数据);
failbit=false;
}
};
有几件事:

  • 您需要在构造函数中初始化
    mutex
    ,并在析构函数中用
    pthread\u mutex\u 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);/*…*/}


至于问题2:通过锁定互斥锁来序列化并发访问。其中一个竞争线程将在获取互斥锁时休眠。

您已经掌握了基本知识,但我会更进一步,将互斥锁本身包装在自己的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(); 
  } 
}; 
#包括
#包括“pthread.h”
类ProtectedMutex
{
pthread_mutex_t&mutex;
公众:
ProtectedMutex(pthread\u mutex\u t&m)
:互斥(m);
{
pthread_mutex_lock(&mutex);
}
~ProtectedMutex()
{
pthread_mutex_unlock(&mutex);
}
};
模板
类ProtectedBuffer{
std::deque缓冲区;
pthread_mutex_t mutex;
公众:
void add_back(T数据){
ProtectedMutex m(互斥);
缓冲区。推回(数据);
} 
void get_front(T&data){
ProtectedMutex m(互斥);
data=buffer.front();
buffer.pop_front();
} 
}; 

“将结果放入主线程定期轮询的缓冲区”-CPU浪费和延迟

“我走错方向了吗?”-是的。我不知道您的系统对辅助线程GUI线程通信有什么支持,但总是有PostMessage()API

当然,您需要一个缓冲区类,其中包含用于串行rx数据的数据成员和执行协议/“处理数据”的方法。你不需要太多其他东西。在第二个线程中,创建一个缓冲区类实例。加载它,处理数据和PostMessage/disp
#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();
  }
};