代码之家  ›  专栏  ›  技术社区  ›  Holograham

C++线程问题

  •  3
  • Holograham  · 技术社区  · 15 年前

    我有一个对象foo,它有一个全局变量time currentTime

    foo有两个方法,从不同的线程调用。

    update()
    {
        currentTime = currentTime + timeDelay;
    }
    
    restart(Time newTime)
    {
        currentTime = newTime;
    }
    

    我看到了重新启动时的行为,时间正确地改变,以及其他当前时间似乎没有重置的时间(或者它确实重置了,但是更新会以某种方式将其设置回原处)。

    方法更新大约每秒调用一次,而重新启动仅在用户启动重新启动事件(按下按钮)时发生。我认为这是一个线程时间问题,任何关于正在发生的事情的建议或评论都是受欢迎的。

    7 回复  |  直到 15 年前
        1
  •  9
  •   1800 INFORMATION    15 年前

    你当然有比赛条件。最严格的解决方案是保护共享变量的使用 currentTime 用一把锁。我正在使用boost.threads mutex类:

    class Foo
    {
      boost::mutex _access;
      update()
      {
        boost::mutex::scoped_lock lock(_access);
        currentTime = currentTime + timeDelay;
      }
    
      restart(Time newTime)
      {
        boost::mutex::scoped_lock lock(_access);
        currentTime = newTime;
      }
    };
    
        2
  •  1
  •   anio    15 年前

    线程1调用update,获取当前时间的副本并将其保存在线程本地内存中。 线程2调用restart,将currentTime设置为newTime。螺纹2饰面。 线程1继续,将currentTime重新分配给其线程本地内存中的currentTime(这是重新启动调用之前currentTime的旧值)+timeDelay。螺纹1现在完成。

    因此,重新启动将失败。还有许多其他情况会导致意外行为。始终同步不同线程之间共享的变量,以避免此类问题。

    您应该使用其他人建议的互斥体。

        3
  •  0
  •   Justicle    15 年前

    在每个函数中添加一些printf以创建正在发生的事情的日志。

    例如,如果在“currentTime=newTime;”之后的另一个线程中立即执行update(),您希望发生什么?-或者更糟- 在期间 那一行的任务。

    完成后,看看: http://en.wikipedia.org/wiki/Mutual_exclusion

        4
  •  0
  •   Thanatos    15 年前

    从两个线程访问同一个变量(正如您所做的)需要某种同步。使用互斥体确保一次只有一个线程访问该变量,即:

    update()
    {
        // Lock mutex
        currentTime = currentTime + timeDelay;
        // unlock mutex
    }
    // Same idea for restart()
    

    问题是,在没有同步原语(如互斥体)的情况下,对同一个变量的访问对于线程是有问题的。假设update()读取currentTime,执行加法,但是在它可以存储结果之前,我们切换线程,restart()执行它。现在我们切换回update(),它将添加的结果(现在无效)写回currentTime,覆盖restart()的工作。互斥体通过允许您保证操作是原子的来防止这种情况。谷歌的一些教程-你需要知道很多其他的事情,如死锁。

    创建/锁定互斥体的具体方式取决于您的操作系统/要使用的库。本机解决方案是*nix系统上的pthreads,Win32上的关键部分。(存在针对Win32的pthreads实现)Boost库也有一个线程部分。

        5
  •  0
  •       15 年前

    在这种情况下,使用互斥对我来说太重了。我会用interlockedexchange和interlockedadd代替。

        6
  •  0
  •   AndrewB    15 年前

    您有一个竞争条件,因为在进入关键区域时没有锁定,并且每个区域都在更新当前时间。每个线程都有一个内存,当一个线程需要从共享内存中获取某些内容时,它会将其复制到本地内存中。第一步是首先获取一个锁,然后清除它的内存,确保变量将从共享内存中加载。现在在关键区域中操作,一旦完成,就解锁关键区域,确保局部变量将写入共享内存。既然你没有锁,你就无法预测会发生什么。

    使用互斥将是您的情况所需要的,因为只有一个键用于当前的时间变量。另一种锁是允许多个键的信号量。

        7
  •  0
  •   TimW    15 年前

    如果当前时间是真的 全局变量 就像你说的那样,你需要一个 全局互斥 保护变量。(pthread_mutex_initializer或boost.call_once construction)

    在这种情况下,boost.threads示例是不正确的,因为生活在不同线程中的foo类的两个实例将具有不同的\u access mutex实例(我真的不喜欢\prefex!)并将锁定自己的实例,而不保护当前时间变量。

    如果当前时间是一个实例变量,那么boost.threads示例是正确的。