0

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

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

线程二访问对象 O 并从双端队列中取出并删除数据并递减计数器变量。

事实证明,这会产生意想不到的结果,因为一个线程告诉我双端队列中有 x 个元素,而另一个线程告诉我没有元素。我想我必须使用某种同步。我尝试使用信号量,但我一定误解了它,因为它不起作用(http://msdn.microsoft.com/en-us/library/windows/desktop/ms686946(v=vs.85).aspx)。

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

到目前为止,我只知道信号量和互斥量,但从未使用过它们,请多多指教。

4

2 回答 2

1

最简单的方法是以排他方式使用EnterCriticalSection/LeaveCriticalSection围绕您要使用的代码:

CRITICAL_SECTION critSect;

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

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

于 2012-10-07T19:51:00.050 回答
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. Astd::mutex用于确保一次只有一个线程访问队列。
  2. 当将某些东西放入队列时,会获取互斥锁上的锁并将对象插入队列中。插入对象后,锁会自动释放,并发出条件变量信号。
  3. 当从队列中提取某些东西时,会获取互斥锁上的锁,并且线程使用条件变量等待队列非空:如果队列中已经有东西,则条件将true立即wait()返回,否则线程进入睡眠状态,直到它收到一个信号,此时重新评估条件。请注意,条件可能会被多次评估,因为条件变量可能会被虚假唤醒。
  4. lambda 通过值捕获其上下文:它真正捕获的只是this; 成员变量不能直接被捕获,因为它们不是本地上下文的一部分。
  5. 结果 frompop()是按值返回的:在锁被释放的那一刻,容器可能会发生变化,使得无法通过引用返回对象,即使它们被放置在合适的位置。

这有点像一个玩具示例的主要原因是它没有关闭系统的好方法:如果一个线程在队列中被阻塞,它会等待直到有另一个对象。一个真正的实现会有某种方式来表明是时候关闭了,可能会从pop(). 此外,有时使用不强制阻塞提取对象的队列很有用。相反,它会有一个try_pop()获取锁的函数,检查队列是否为非空,并根据结果提取和对象或信号失败。不过,这样的功能很容易实现。

于 2012-10-07T20:55:08.123 回答