将互斥保护构建到C ++类中的线程安全方法? [英] Thread-safe way to build mutex protection into a C++ class?

查看:67
本文介绍了将互斥保护构建到C ++类中的线程安全方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to implement a producer/consumer model multithreaded program in C++ for a project I'm working on. The basic idea is that the main thread creates a second thread to watch a serial port for new data, process the data and put the result in a buffer that is periodically polled by the main thread. I've never written multi-threaded programs before. I've been reading lots of tutorials, but they're all in C. I think I've got a handle on the basic concepts, but I'm trying to c++ify it. For the buffer, I want to create a data class with mutex protection built in. This is what I came up with.

1)我会以错误的方式处理吗?有没有更聪明的方法来实现受保护的数据类?

1) Am I going about this the wrong way? Is there a smarter way to implement a protected data class?

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

2) What will happen in the following code if two threads try to call ProtectedBuffer::add_back() at the same time?

#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);
  }
};

感谢您提出的所有宝贵建议.我试图在下面实现它们.我还添加了一些错误检查,因此,如果某个线程设法以某种方式尝试将同一互斥锁锁定两次,则它将正常失败.我想.

Thanks for all the great suggestions. I've tried to implement them below. I also added some error checking so if a thread somehow manages to try to lock the same mutex twice it will fail gracefully. I think.

#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_mutex_init 初始化mutex并在析构函数中使用pthread_mutex_destroy释放它.

  • You need to initialize mutex with pthread_mutex_init in the constructor and free it with pthread_mutex_destroy in the destructor.

您必须使您的类不可复制且不可分配(否则,请正确实现副本构造函数和赋值运算符;请参见上文).

You must make your class non-copyable and non-assignable (or otherwise implement copy constructor and assignment operator correctly; see above).

值得为锁创建SBRM帮助程序类:

It's worthwhile making a SBRM helper class for the lock:

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); /* ... */ }那样创建一个同步作用域.

Now you can make a synchronized scope like { Lock lk(mutex); /* ... */ }.

对于问题2:并发访问通过锁定互斥锁来序列化.竞争线程之一将在获取互斥锁后进入休眠状态.

As for Question 2: Concurrent access is serialized by means of locking the mutex. One of the competing threads will sleep on the acquisition of the mutex lock.

这篇关于将互斥保护构建到C ++类中的线程安全方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆