多线程全局对象访问

时间:2012-10-07 19:42:51

标签: c++ windows multithreading

好的,我有一个有两个额外线程的应用程序。

线程1访问对象O并将数据插入到作为对象O的一部分的双端队列中并递增计数器变量。

线程2访问对象O并从deque中删除并删除数据并减少计数器变量。

由于一个线程告诉我deque内部有x个元素而另一个线程告诉我没有元素,因此结果出现了意想不到的结果。我假设我必须使用某种同步。我试图使用信号量,因为它不起作用,我必须误解它(http://msdn.microsoft.com/en-us/library/windows/desktop/ms686946(v=vs.85).aspx)。

所以我想知道如何从两个线程访问全局对象。请注意,对全局对象O的访问经常发生,因为访问是在while循环内导致继续插入和轮询。 (可能的解决方案是否阻止其他线程访问对象,因此阻止while循环?)

到目前为止,我只知道信号量和互斥量,但从未使用过任何信号量,请非常友好并启发我。

2 个答案:

答案 0 :(得分:1)

最简单的方法是在您想要以独占方式使用的代码周围使用EnterCriticalSection / LeaveCriticalSection

CRITICAL_SECTION critSect;

// Later in the code
EnterCriticalSection(&critSect);
// Do stuff with O
LeaveCriticalSection(&critSect);

你当然会在两个线程中使用它。一次只能有一个线程进入/离开区域。 EnterCriticalSection将阻塞,直到另一个线程调用LeaveCriticalSection

答案 1 :(得分:0)

当从一个访问可能更改对象的一个​​访问权限访问多个线程中的对象时,您需要一些来自同步的对象。对于所描述的场景,您可能希望拥有一个队列类,它可以执行必要的线程保护和信令。这是一个简单的实现:

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

template <typename T>
class queue
{
private:
    std::mutex              d_mutex;
    std::condition_variable d_condition;
    std::deque<T>           d_queue;
public:
    void push(T const& value) {
        {
            std::unique_lock<std::mutex> lock(this->d_mutex);
            d_queue.push_front(value);
        }
        this->d_condition.notify_one();
    }
    T pop() {
        std::unique_lock<std::mutex> lock(this->d_mutex);
        this->d_condition.wait(lock, [=]{ return !this->d_queue.empty(); });
        T rc(std::move(this->d_queue.back()));
        this->d_queue.pop_back();
        return rc;
    }
};

该代码使用C ++ 2011构造,但可以轻松更改以避免使用它们,而是使用C ++ 2003构造,而不是这些构造不是标准化的。

关键点是:

  1. std::mutex用于确保仅在线程上一次访问队列。
  2. 将某些内容放入队列时,会获取互斥锁上的锁,并将该对象插入队列中。插入对象后,将自动释放锁定并发出条件变量信号。
  3. 从队列中提取内容时,会获取互斥锁上的锁,并且线程使用条件变量等待队列非空:如果队列中已存在某些内容,则条件为{{1} }和true立即返回,否则线程将进入休眠状态,直到它获得一个信号,此时重新评估条件。请注意,可能会多次评估条件,因为可能会对条件变量进行虚假唤醒。
  4. lambda按值捕获其上下文:它实际捕获的全部是wait();成员变量无法直接捕获,因为它们不属于本地上下文。
  5. this的结果由值返回:锁释放后,容器可能会被更改,无法通过引用返回对象,即使它们被放入合适的位置。
  6. 这有点像一个玩具示例的主要原因是它没有一个很好的方法来关闭系统:如果一个线程被阻塞在队列上,它会等待,直到有另一个对象。一个真正的实现可以通过某种方式表明是时候关闭,可能会从pop()抛出异常。此外,有时让队列不强制阻塞来提取对象也很有用。相反,它将有一个函数pop(),它获取一个锁,检查队列是否为非空,并根据结果提取和对象或信号失败。但是,这样的函数很容易实现。