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

让多个线程将相同的值写入相同的变量可以吗?

  •  3
  • jake  · 技术社区  · 16 年前

    我了解竞争条件,以及多个线程访问同一个变量时,一个线程所做的更新如何被其他线程忽略和覆盖,但是如果每个线程将相同的值(而不是不同的值)写入同一个变量,甚至会导致问题吗?此代码是否可以:

    globalvar.property=11;

    (假设该属性永远不会被分配除11以外的任何内容),如果多个线程同时执行该属性会导致问题吗?

    7 回复  |  直到 7 年前
        1
  •  8
  •   Torne    16 年前

    问题是当你读到那个状态,然后做些什么的时候。写作是一条红鲱鱼——的确,只要这是一个单词,大多数环境都会保证写作是原子的,但这并不意味着包含此片段的较大代码片段是线程安全的。首先,假设您的全局变量包含一个不同的开始值-否则,如果您知道它总是相同的,为什么它是一个变量?第二,也许你最终 阅读 这个值又回来了?

    问题可能是,你写这段共享状态是有原因的——是为了表明发生了什么事?这就是问题的症结所在:当没有锁结构时,根本就没有隐含的内存访问顺序。很难指出这里的错误,因为您的示例实际上不包含 使用 关于这个变量,这里有一个类似于中性C的语法的小例子:

    int x = 0, y = 0;
    
    //thread A does:
    x = 1;
    y = 2;
    if (y == 2)
        print(x);
    
    //thread B does, at the same time:
    if (y == 2)
        print(x);
    

    线程A总是打印1,但线程B打印0是完全有效的。线程A中的操作顺序只需要从线程A中执行的代码中可以观察到-线程B可以看到状态的任何组合。对x和y的写入可能实际上不是按顺序进行的。

    即使在单处理器系统上也可能发生这种情况,因为大多数人不希望这种重新排序—编译器可能会为您重新排序。在SMP上,即使编译器不重新排序,内存写入也可能在不同处理器的缓存之间重新排序。

    如果这似乎不能回答你的问题,在问题中包括你的例子的更多细节。如果没有变量的使用,就不可能确定这样的使用是否安全。

        2
  •  2
  •   Community CDub    8 年前

    这取决于该声明实际完成的工作。仍然有一些情况发生,例如,如果C++类重载了=运算符,并在该语句中做任何非平凡的事情。

    我无意中用pod类型(内置的原始类型)编写了这样的代码,它运行得很好——但是,这绝对不是一个好的实践,而且我不相信它是可靠的。

    当你使用这个变量的时候,为什么不把它的内存锁起来呢?事实上,如果您不知何故“知道”这是代码中唯一可能在某个时刻出现的write语句,为什么不直接使用值11,而不是将其写入共享变量? ( 编辑: 我想最好用一个固定的名字而不是 magic number (顺便说一句,直接在法典中)

    如果您使用这个来计算至少一个线程何时到达这个语句,那么您可以使用从1开始的信号量,并由第一个到达它的线程递减。

        3
  •  1
  •   Laurie Young    16 年前

    我预计结果不会确定。因为不同的编译器,不同的语言,不同的操作系统,不同的操作系统等等,所以不,它是不安全的。

    但是,为什么要这样做呢-添加一行代码来获取互斥锁(在大多数语言中都是一行或两行代码),这将消除任何可能出现的问题。如果这是两个昂贵的,那么你需要找到另一种解决问题的方法

        4
  •  1
  •   Benoit    16 年前

    一般来说,除非您的系统提供原子操作(保证在一个周期内执行的操作),否则这被认为是不安全的。 原因是,虽然“c”语句看起来很简单,但通常会发生一些底层的汇编操作。

    根据你的操作系统,你可以做一些事情:

    • 采用互斥信号量(mutex)来保护访问
    • 在某些操作系统中,您可以暂时禁用抢占,这保证您的线程不会交换出去。
    • 有些操作系统提供的writer或reader信号量比普通的旧互斥锁更具性能。
        5
  •  1
  •   teeks99    16 年前

    这是我对这个问题的看法。

    您有两个或多个线程正在运行,这些线程向变量写入数据……比如状态标志之类的,您只想知道其中一个或多个线程是否为真。然后在代码的另一部分(线程完成后),您要检查并查看是否至少在线程上设置了该状态…例如

    bool flag = false
    threadContainer tc
    threadInputs inputs
    
    check(input)
    {
        ...do stuff to input
        if(success)
            flag = true
    }
    
    start multiple threads
    foreach(i in inputs) 
       t = startthread(check, i)
       tc.add(t)  // Keep track of all the threads started
    
    foreach(t in tc)
        t.join( )  // Wait until each thread is done
    
    if(flag)
       print "One of the threads were successful"
    else
       print "None of the threads were successful"
    

    我相信上面的代码是可以的,假设您可以不知道哪个线程将状态设置为true,并且您可以在读取该标志之前等待所有多线程的工作完成。但我可能错了。

        6
  •  0
  •   Chris Vest    16 年前

    假设这个属性除了11个之外永远不会被分配,那么我一开始就看不到分配的理由。那就把它定下来。

    只有当您打算更改值时,assignment才有意义 除非 行为本身也有其他副作用,比如易失性写在Java中具有内存可见性的副作用。如果更改多个线程之间的共享状态,则需要同步或“处理”并发问题。

    如果在没有正确同步的情况下,为多个线程之间共享的某个状态分配一个值,则无法保证其他线程何时会看到该更改。没有可见性保证意味着其他线程可能 从未 见分配人。

    编译器,jit,cpu缓存。他们都在试图让你的代码尽可能快的运行,如果你对内存可见性没有任何明确的要求,那么他们会利用这一点。如果不在你的机器上,那就找别人。

        7
  •  -1
  •   Nicholas Mancuso    16 年前

    如果手术是原子的,你 应该 能过得很好。但实际上我不会那样做。最好在对象上获取一个锁并写入值。