【问题标题】:Mutex lock on separate classes不同类的互斥锁
【发布时间】:2015-11-03 17:28:11
【问题描述】:

互斥锁的正确用法是什么?我怎么知道它在保护什么?它是否会在锁定期间停止所有线程运行?

例如,我有一个包含对象列表的单例。

class Foo
{
    // Pretend this is a singleton.
    std::list<Object>* GetObjects() { return &objects; }
    void Reset() { for ( auto iter = objects.begin(); iter != objects.end(); ++iter ) { delete *iter; } }
    std::list<Object> objects;
};

在一个单独的类中,我有一个更新方法。这是在单独的线程中运行的。

class Bar
{
    void Update()
    {
        std::list<Object>* objects Foo.GetObjects(); 
        for(auto iter = objects.begin(); iter != objects .end(); ++iter )
            iter->SetTransform(50,50,50);
    }
};

我的问题是我想在 Foo 上调用 Reset() 并删除所有对象。但是我的 Bar 类正在更新所有这些对象。因此,如果它在更新方法的中间,它可能会尝试修改已删除的对象。

如何防止这种情况发生?

我可以在重置功能中创建一个互斥锁并在删除之前将其锁定并在之后将其解锁吗?像这样的:

 void Reset()
 {
     Mutex mutex; mutex.Lock()
     for ( auto iter = objects.begin(); iter != objects.end(); ++iter )
         delete *iter;
     mutex.Unlock();
}

这会让其他班级知道这些资源被锁定了吗?我不确定互斥锁如何知道要保护哪些信息。它如何知道它需要在单例类中锁定列表。

顺便说一句,这都是伪代码,因此存在语法错误和缺少信息。

【问题讨论】:

标签: c++ multithreading


【解决方案1】:

简单来说,互斥锁是一种锁定机制。

说,你有一个资源,在你的情况下list&lt;object&gt; objects; 现在,如果多个线程最终同时在这个列表上操作,那么这些操作的结果可能是出乎意料的。

所以,基本上互斥体为我们提供了一种机制,可以确保一次只有一个线程对受保护资源进行操作。

实施:

在Linux内核中,线程由task_struct对象建模,互斥锁实现如下:

struct mutex {
  /* 1: unlocked, 0: locked, negative: locked, possible waiters */
  atomic_t                count;
  spinlock_t              wait_lock;
  struct list_head        wait_list;
 #if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER)
  struct task_struct      *owner;
 #endif
}

所以,第一个尝试锁定互斥锁的线程变成了owner,其他所有线程都被添加到该互斥锁的wait_list

一旦所有者释放它,来自wait_list 的下一个任务将获得下一次机会。

注意

如果是互斥锁,谁锁定它就必须解锁它,

否则其他所有人都将被永远锁定,也称为dreadlock

【讨论】:

    【解决方案2】:

    互斥锁的正确用法是什么?

    同步对共享资源的访问。

    我怎么知道它在保护什么?

    通过阅读代码。获得锁时访问的内容就是受保护的内容。

    它会阻止所有线程在锁定期间运行吗?

    没有。只有等待互斥锁的人才会被“阻止”。如果其他线程已经拥有锁,则当您调用Lock() 时,线程将等待。它将等到拥有锁的人调用Unlock()。如果没有人拥有锁,那么调用Lock() 的线程会获取它并且不会等待。如果您有一个锁,则多个线程可能正在等待,并且不一定定义解锁时谁获得它。请参阅特定框架的文档。

    如何防止这种情况发生?我可以在重置功能中创建一个互斥锁并在删除之前将其锁定并在之后解锁吗?

    如果您获取并释放对访问共享资源的锁定,则可以防止多个线程同时访问该共享资源。所以,是的,您可以围绕重置代码锁定和解锁,但您需要在所有访问共享资源的地方进行。

    在这种情况下,objects 是您的共享资源。如果您在删除objects 的内容的地方获取锁,您还必须在您使用它的地方获取锁。由于您通过GetObjects() 提供了引用,因此您还需要让调用者意识到需要通过此互斥锁同步访问。

    另一种方案是摆脱GetObjects() 并使用其他方法,例如Object *get(int index),获取/设置/操作锁内的数据,并且从不实际给出对objects 的原始引用。

    这会让其他班级知道这些资源被锁定了吗?我不确定互斥锁如何知道要保护哪些信息。

    没有。互斥体什么都不知道。您必须对代码施加适当的语义,要么为调用者记录代码,要么通过知道获取 Mutex 的对象对共享资源的所有访问进行重传。

    【讨论】:

    • 必须是同一个互斥体吗?或者它可以是用于 reset() 和 update() 的单独互斥锁。
    • 必须是同一个。您现在遇到的问题是您获得了锁来执行重置操作,但没有其他人等待锁被释放。如果重置操作正在运行,则通过在访问对象之前锁定互斥锁,另一个线程将不得不等待重置完成,然后 Lock() 返回并在第一个线程上继续执行。如果您创建两个不同的互斥体,那么您基本上会遇到相同的问题,因为如果有人锁定了 mutexB,mutexA.Lock() 将不会等待。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    • 2011-06-10
    • 2011-07-24
    • 2012-12-25
    • 2010-11-22
    相关资源
    最近更新 更多