从不同线程调用时,Singleton属性值错误

时间:2018-11-01 09:07:36

标签: c# wpf multithreading singleton

对于我的选项的ViewModel,我具有以下Singleton模式:

    private static volatile GeneralOptionsViewModel instance;
    private static object syncRoot = new object();
    /// <summary>
    /// threadsave singleton
    /// </summary>
    public static GeneralOptionsViewModel Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                        instance = new GeneralOptionsViewModel();
                }
            }

            return instance;
        }
    }

在我的XAML中,我有一个扩展工具包中的颜色选择器:

<xctk:PropertyGridEditorColorPicker Background="Transparent" Name="face" 
Margin="5,0" Width="50" BorderBrush="#32FFFFFF" BorderThickness="1" 
SelectedColor="{Binding FaceRectColor, Mode=OneWayToSource, 
UpdateSourceTrigger=PropertyChanged}"/>

如您所见,它绑定到GeneralOptionsViewModel类的FaceRectColor属性,该属性定义如下。 在设置器中,将转换为MCvScalar(也是同一个类的属性),这是我以后对应用程序需要的格式:

    public Color FaceRectColor
    {
        get
        {
            return faceRectColor;
        }
        set
        {
            if (faceRectColor != value)
            {
                faceRectColor = value;
                FaceRectColorScalar = new MCvScalar(value.B, value.R, value.G, value.A);
                SetProperty(ref faceRectColor, value);
            }
        }
    }

我现在的问题是,绑定有效,并且正确的值也写入变量,但是当我使用具有不同类(和不同线程)的属性调用单例时,对于所有颜色它总是显示为零渠道。但是,如果我直接在单例类中中断程序,则可以看到正确的值。 AFAIK单例应该是线程安全的,所以我正在寻找这种行为的原因。 我的猜测是一个线程问题,因为单例类的其他属性可以正确显示,但是它们只能在主线程中调用。

编辑:就我而言,单例类的所有属性值都是在工作线程处于活动状态之前设置的。这意味着在工作线程处于活动状态期间不会进行任何更改。

编辑II:Here is the complete project for code evaluation. 在第202行的CameraViewModel类中,是一个函数的相关调用,我想在其中传递单例中的值。

2 个答案:

答案 0 :(得分:1)

当您的属性更改并且在另一个线程上进行更改时,为通知所有人(尤其是UI)此更改而进行的调用正在调用线程中运行。在不是UI线程的线程中访问UI是一个坏主意。有时可能会起作用。但是它迟早会失败。

当前问题的解决方案是更改UI线程中的属性。

也就是说,也许您应该考虑是否需要一个Singleton。这是一个巨大的危险信号,表明程序的结构有问题。您不需要 单身人士。如果其他上下文具有第二个设置viewmodel,则不会发生任何不良情况。您似乎想要一个Singleton,因为拥有全局变量非常容易。那就是单例的缺点。您要购买缺点,因为您需要此模式中的某些功能。如果您发现使用此模式仅仅是因为它的缺点给了您借口使其具有全局变量,那么您就在做错误的模式。 It's an anti-pattern

答案 1 :(得分:1)

您的“单身人士”包含一个公共构造函数,这实际上使它成为非单身人士。而且您没有绑定到GeneralOptionsView中的单例。

如果您确实希望GeneralOptionsViewModel是单身人士,则应该这样实现:

public sealed class GeneralOptionsViewModel : ViewModelBase
{
    private static readonly GeneralOptionsViewModel _instance = new GeneralOptionsViewModel();
    private GeneralOptionsViewModel()
    {
        GetAvailableCameraList();
        DetectorTypeList = new List<string>() { "Cascade Detector" };
        SelectedDetectorTypeIndex = 0;
    }

    public static GeneralOptionsViewModel Instance => _instance;

    //...
}

然后应将视图的DataContext设置为单例:

<Grid DataContext="{Binding Source={x:Static local:GeneralOptionsViewModel.Instance}}">