如何将一个方法标记为非线程安全的?

4

我偶尔会遇到这个问题并忽略它,但今天它开始困扰我了。

private readonly object _syncRoot = new object();

private List<int> NonconcurrentObject { get; } = new List<int>();

public void Fiddle()
{
    lock (_syncRoot)
    {
        // ...some code...
        NonconcurrentObject.Add(1);
        Iddle();
    }
}

public void Twiddle()
{
    lock (_syncRoot)
    {
        // ...some different code...
        NonconcurrentObject.Add(2);
        Iddle();
    }
}

private void Iddle()
{
    // NOT THREADSAFE! DO NOT CALL THIS WITHOUT LOCKING ON _syncRoot
    // ......lots of code......
    NonconcurrentObject.Add(3);
}

我有一个类的多个公共方法,其中某些代码本质上不是线程安全的(上面的List是一个微不足道的例子)。 我想要使用辅助方法来处理它们之间共享的代码(任何人都会这样做),但在拆分共享代码时,我面临着一个困境:是否在辅助方法中使用递归锁定? 如果我这样做,我的代码就会浪费资源,可能表现较差。 如果我不这样做(如上所述),则辅助方法将不再具有线程安全性,并且如果将来由其他方法调用,则会存在严重的竞态条件。

我如何(优雅而强大地)表明方法不是线程安全的?


您可以使用自定义属性和Roslyn分析器,但这取决于情况,可能过于复杂。 - yaakov
1
@yaakov:我很想看到那个答案。 - Joshua
XY问题。只需使用线程安全的集合即可。 - user585968
2
@MickyD 使用线程安全的集合只在简单情况下是可行的,那种情况下集合的内部状态是您需要保护的唯一共享状态。因为线程安全的集合只能保证其内部状态的完整性。当您需要执行任何复杂操作时,通常需要进行手动同步,因为有其他需要保护的共享状态。 - Theodor Zoulias
@MickyD 一些评论指出OP的代码并不是简单的:// ...一些代码...// ...一些不同的代码...。这些都在受保护的区域内。 - Theodor Zoulias
显示剩余3条评论
3个回答

3
您使用文档注释。
///<remarks>not thread safe</remarks>

1
你知道的。这就是答案。我在这里绝对荒谬。 - The Most Curious Thing
我更喜欢这个答案,因为创建一个属性来标记方法会向我传达与该属性相关的某种行为。MSDN提供了文档,说明某个类是线程安全的还是不安全的。完全依赖于文档。 - Reap

1

您可以使用自定义属性来标记不是线程安全的方法。

与注释相比的优点是,如果您希望在以后进行进一步处理(通过反射),它会为您提供选项。

public class NotThreadSafe : Attribute
{
    //...
}

public class MyClass
{
    [NotThreadSafe]
    public void MyMethod()
    {
        //...
    }
}

对我来说,这种方法会传达一种与标记方法属性相关的行为,因为它被编译了。更好的方法是使用注释和XML样式注释进行文档化,这对于公共API来说更好。 - Reap

0

对于没有使用锁保护的实用方法,您可以添加_Unsafe后缀。

优点:它提醒您正在进行危险操作,因此必须格外小心。一个小错误可能会在未来花费您数天的调试时间。

缺点:不太美观,可能会与unsafe关键字混淆。

private void Iddle_Unsafe()
{
    NonconcurrentObject.Add(3);
}

public void Twiddle()
{
    lock (_syncRoot)
    {
        NonconcurrentObject.Add(2);
        Iddle_Unsafe();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接