Skip to main content
 首页 » 编程设计

c#之双重检查锁定可能不正确的实现

2024年02月05日33kuangbin

我有一个线程安全类:

internal class ExmplFile  
{ 
    private readonly string filename; 
    private int resolution; 
    private volatile PaddedImage gaussian; 
    private object lockObject=new object(); 
 
    //blah, blah, blah 
 
    internal PaddedImage Gaussian() 
    { 
        if (gaussian != null) 
        { 
            return gaussian; 
        } 
        lock (lockObject) 
        { 
            if (gaussian == null) 
            { 
                Image(); 
                if (File.Exists(filename + "-gaus.raw")) 
                { 
                    gaussian = LoadImage(filename + "-gaus.raw", TerraGodContext.Instance() 
                        .Config.PpaCandidateRange); 
                    gaussian.ConformRepeatPadding(); 
                } 
                else 
                { 
                    gaussian = new PaddedImage(resolution, resolution, false,  
                        TerraGodContext.Instance().Config.PpaCandidateRange); 
                    PaddedImage temp = new PaddedImage(resolution, resolution, false,  
                        TerraGodContext.Instance().Config.PpaCandidateRange); 
                    ImageProcessing.CalcGaussian(image,gaussian,temp, 16f* resolution 
                        /TerraGodContext.Instance().Config.ExmplResDS); 
                } 
            } 
        } 
        return gaussian; 
    } 
 
} 

Resharper 给我三个警告:

  1. 在 gaussian.ConformRepeatPadding() 中的 gaussian 上它说 双重检查锁定的可能不正确的实现。读取选中字段的权限。。
  2. 在 gaussian = new PaddedImage( 中的 gaussian 上,它说 双重检查锁定的可能不正确的实现。可能对检查的字段进行多次写入访问。.
  3. 在 ImageProcessing.CalcGaussian(image,gaussian,temp, 中的 gaussian 上显示双重检查锁定的可能不正确的实现。读取检查字段的权限。 (与第 1 条警告相同)。

是我傻还是 Resharper 傻?

PS:如果我遗漏了一些重要的东西,这里是完整的类代码,其中包含我在上面省略的部分。

using System; 
using System.IO; 
using System.Text; 
 
namespace UPlus.TerrEngine 
{ 
internal class ExmplFile : IEquatable<ExmplFile> 
{ 
    private readonly string filename; 
    private int resolution; 
    private volatile PaddedImage image; 
    private volatile PaddedImage gaussian; 
    private object lockObject=new object(); 
    public ExmplFile(string abstractFileName, int res) 
    { 
        filename = new StringBuilder(abstractFileName).Append("-").Append(res.ToString("D4")).ToString(); 
        resolution = res; 
    } 
 
    internal FilledList<MatchItem>[] GetMatchItems(int groupIdx, int regionIdx) 
    { 
        AlgorithmConfig Config = TerraGodContext.Instance().Config; 
        if (resolution !=Config.ExmplResDS) throw new Exception(); 
        PpaGraph exmplGraph = new PpaGraph(Config.ExmplResDS, Config.ExmplResDS, Config.ExmplResDS 
            / Config.PpaExmplResDS); 
        exmplGraph.Calculate(Image(), true, false, DebugOpts.BranchMin); 
        exmplGraph.PrepareGraphForMatch(Config.ExmplCptRadius, Config.ExmplProcNodeDistance); 
        exmplGraph.CalcExmplProcNodeGroups(groupIdx); 
        MatchItemFinder matchItemFinder=new MatchItemFinder(Image(),Config.ExmplPatchSizeDS,true); 
        matchItemFinder.Init(exmplGraph.processNodesGroups, regionIdx); 
        return matchItemFinder.matchItems; 
    } 
 
    internal FastList<MatchItem> LoadMatchItems() 
    { 
        throw new Exception(); 
    } 
 
    internal PaddedImage Image() 
    { 
        if (image != null) 
        { 
            return image; 
        } 
        lock (lockObject) 
        { 
            if (image == null) 
            { 
                image=LoadImage(filename + ".raw", TerraGodContext.Instance().Config 
                    .PpaCandidateRange); 
            } 
        } 
        return image; 
    } 
    internal PaddedImage Gaussian() 
    { 
        if (gaussian != null) 
        { 
            return gaussian; 
        } 
        lock (lockObject) 
        { 
            if (gaussian == null) 
            { 
                Image(); 
                if (File.Exists(filename + "-gaus.raw")) 
                { 
                    gaussian = LoadImage(filename + "-gaus.raw", TerraGodContext.Instance() 
                        .Config.PpaCandidateRange); 
                    gaussian.ConformRepeatPadding(); 
                } 
                else 
                { 
                    gaussian = new PaddedImage(resolution, resolution, false,  
                        TerraGodContext.Instance().Config.PpaCandidateRange); 
                    PaddedImage temp = new PaddedImage(resolution, resolution, false,  
                        TerraGodContext.Instance().Config.PpaCandidateRange); 
                    ImageProcessing.CalcGaussian(image,gaussian,temp, 16f* resolution 
                        /TerraGodContext.Instance().Config.ExmplResDS); 
                } 
            } 
        } 
        return gaussian; 
    } 
 
    private PaddedImage LoadImage(string fileName, int padding) 
    { 
        PaddedImage img=new PaddedImage(resolution,resolution,false,padding); 
        img.LoadRaw(fileName); 
        img.ConformRepeatPadding(); 
        return img; 
    } 
 
    public bool Equals(ExmplFile other) 
    { 
        return filename == other.filename; 
    } 
 
    public override int GetHashCode() 
    { 
        return filename.GetHashCode(); 
    } 
  } 
} 

编辑:以下是截图以防情况不清楚:

请您参考如下方法:

一般来说,在 lock 内,您应该使用局部变量并将写入选中的字段作为最后可能的操作。

不然你在中间对它做什么?例如。 ConformRepeatPadding() 是做什么的?我猜它有副作用,因为它没有返回值?但是当您调用它时,您已经通过将它的值分配给该字段来公开该对象。

如果您过早地分配给该字段,其他线程可能会在“部分构造”状态下观察到该对象,因此正如我所说,您通常应该将写入该字段作为 lock 中最后可能的操作。