如果我只想以线程安全的方式测试和设置标志,那么线程类是啥?

Posted

技术标签:

【中文标题】如果我只想以线程安全的方式测试和设置标志,那么线程类是啥?【英文标题】:What is the threading class if I just want to test and set a flag in a threadsafe manner?如果我只想以线程安全的方式测试和设置标志,那么线程类是什么? 【发布时间】:2011-01-05 13:43:50 【问题描述】:

我只想做一个简单但线程安全的布尔测试(和设置) 所以:

if(myBoolean==false)   //should not lock/wait!
 
     myBoolean=true;
     .....

else

     ....

我考虑了以下(虽然可能不正确,所以请纠正我误解的地方)

使用 Lock if(myBoolean)... 构造似乎是一种矫枉过正的方法。而且,它还在等待锁空闲时锁定线程。我不想要这个。 AutoResetEvent 类确实有一个布尔状态的概念,但它用于向另一个正在等待的线程发出信号。所以与我的情况无关 Semaphore 类有一个引用计数的概念(可能是为了限制对资源的访问量?)。所以可能不是我想要的。 互斥类。据我了解,这与 Lock 原语的原理相同

有人知道什么是类/构造可以有效地做到这一点吗?

【问题讨论】:

为什么要检查是不是true,然后又设置成true 如果你希望它是线程安全的,你必须降低一些效率。锁定它。 @slaks: 好点...已修复;^) @danail: Lock 将线程设置为等待状态...我不想要这个。 【参考方案1】:

考虑Interlocked.CompareExchange。

【讨论】:

【参考方案2】:

答案 (Interlocked.CompareExchange) 已经给出,但这是我的用法示例:

private int _isDisposing;

public bool IsDisposing

    get
    
        return this._isDisposing != 0;
    


public void Dispose()

    // Side note: I may want to `return` instead of `throw`
    if (Interlocked.CompareExchange(ref _isDisposing, 1, 0) != 0)
        throw new InvalidOperationException("Dispose was recursively called.");

    try
    
        Dispose(true);
        GC.SuppressFinalize(this);
    
    finally
    
        _isDisposing = 0;
    

【讨论】:

文档说“无论是否发生交换,此方法的返回值都是 location1 中的原始值。”。见msdn.microsoft.com/en-us/library/bb297966.aspx【参考方案3】:

您可能正在寻找Interlocked 类,特别是Interlocked.CompareExchange

【讨论】:

@slaks:我不知道我的问题如何更具体。什么是原子 testandset(众所周知的并发模式)的 .net 等价物。我以为我解释得很好。 使用 Interlocked,您可以在原子操作中执行您所谓的“testandset”。【参考方案4】:

要进行线程安全测试和设置操作,代码必须阻塞其他线程。为避免不必要的锁定,您可以使用 test-and-test-and-set 模式:

if (something) 
   lock(_sync) 
      if (something) 
         something = false;
         ...
      
   

需要进行第二次测试以确保其他线程没有更改第一次测试和锁定之间的值。

【讨论】:

但是由于双重测试不是原子的,锁仍然有可能将我的线程设置为等待状态。我真的很希望有一个原子的“testandset”函数。 您是否还必须确保“某物”具有易失性才能使其生效?【参考方案5】:

对于集合,我使用“测试和添加”

    /// <summary>
    /// If h contains v then return true, else add v to h and return false.
    /// Thread safe on h.
    /// </summary>
    /// <param name="h"></param>
    /// <param name="v"></param>
    /// <returns></returns>
    bool TestAndAdd(HashSet<string> h, string v)
    
        lock(h)
        
            if(h.Contains(v))
            
                return true;
            
            h.Add(v);
            return false;
        
    

然后我可以像这样测试和设置:

if (!TestAndAdd(usedCodes, mc.code))

【讨论】:

【参考方案6】:

我多次遇到同样的问题,但我认为我无法记住 Interlocked.CompareAndChange() 的最后一个参数是比较数。 这是我想出的。

using System.Threading;

public class AtomicFlag

    public const int SETVALUE = 1;
    public const int RESETVALUE = 0;

    /// <summary>
    /// Represents the current state of the flag.
    /// 0 means false (or reset).
    /// 1 means true (or set).
    /// </summary>
    private int Value;

    /// <summary>
    /// Creates an atomicflag with the specified default value.
    /// </summary>
    /// <param name="initialValue">AtomicFlag.SETVALUE or 
    /// AtomicFlag.RESETVALUE. Defaults to RESETVALUE.</param>
    public AtomicFlag(int initialValue = RESETVALUE)
    
        Guard.AgainstUnsupportedValues<int>(initialValue, "initialValue",
           new int[]  SETVALUE, RESETVALUE );
        Value = initialValue;
    


    public void Set()
    
        Value = SETVALUE;
    

    public void Reset()
    
        Value = RESETVALUE;
    


    public bool TestAndSet()
    
        // Use Interlocked to test if the current value is RESETVALUE,
        // return true and set value to SETVALUE.
        //
        // From Interlocked.CompareExchange help:
        // public static int CompareExchange(
        //    ref int location1,
        //    int value,
        //    int comparand
        // )
        // where
        //  location1: The destination, whose value is compared with 
        //             comparand and possibly replaced.
        //  value:     The value that replaces the destination value if the
        //             comparison results in equality.
        //  comparand: The value that is compared to the value at
        //             location1. 
        return (RESETVALUE == Interlocked.CompareExchange(
            ref Value, SETVALUE, RESETVALUE));
    

    public bool TestAndReset()
    
        // If the current value is SETVALUE, return true and change value 
        // to RESETVALUE.
        return (SETVALUE ==
            Interlocked.CompareExchange(ref Value, RESETVALUE, SETVALUE));
    

【讨论】:

以上是关于如果我只想以线程安全的方式测试和设置标志,那么线程类是啥?的主要内容,如果未能解决你的问题,请参考以下文章

java并发编程6.取消与关闭

跨越异步边界的布尔标志的线程安全

您可以从另一个线程访问 UI 元素吗? (不设置)

如何使用 Java 在多线程环境中测试某些东西 [重复]

创建线程的3种方式。什么是线程安全。

Java线程安全总结