首页 > 解决方案 > 变量未更新

问题描述

问题:

List<>即使列表中没有可访问的数据,变量也正在通过引用检查。

解决方案:

Save() 函数在保存线程完成导致线程中止之前完成执行。threadrunning = true在启动线程之前添加。

代码:

Thread saveThread;
public string filepath;
public string rootElementName;
public string objectElementName;
public int objectsSaved;
public SaveData saveData;
public List<SavedObject> datasets;
public bool threadRunning = false;
public bool syncingData = false;

public void StartSaveThread()
{
    bool warned = false;

    //The "error" is here
    //The reference returns true but none of the data is accessible, there is nothing in the list
    while(datasets != saveData.objects)
    {
        if(warned == false)
        {
            Debug.Log("Waiting for data to sync");
            syncingData = true;
            warned = true;
        }
    }

    syncingData = false;

    saveThread = new Thread(SaveData);
    saveThread.Start();
}

public void SaveData()
{
    bool saveComplete = false;
    threadRunning = true;

    while (threadRunning && saveComplete == false)
    {
        objectsSaved = 0;

        XmlDocument saveFile = new XmlDocument();

        XmlElement documentRoot = saveFile.CreateElement(rootElementName);

        foreach (SavedObject dataset in datasets)
        {
            XmlElement savedObjectElement = saveFile.CreateElement(objectElementName);

            foreach (SavedElement savedValue in dataset.data.savedElements)
            {
                XmlElement newElement = saveFile.CreateElement(savedValue.name);
                newElement.InnerText = savedValue.value;

                savedObjectElement.AppendChild(newElement);
            }

            documentRoot.AppendChild(savedObjectElement);

            objectsSaved++;
        }

        saveFile.AppendChild(documentRoot);

        saveFile.Save(filepath);

        saveComplete = true;
    }

    threadRunning = false;
}

如果您想重现错误,可以在下面找到更多代码。

SavedObject 类(简化):

//this class is in SavedObject.cs
public int id = 0;

public string name = "";
public string objectPath = "";

public bool saveData;
public bool loadData;

public bool savePosition;
public bool saveRotation;
public bool saveScale;

public SavedObjectData data = new SavedObjectData();

public GameObject parent;

public List<SavedElement> savedElements = new List<SavedElement>();

public void StoreData()
{
    data.id = id;

    data.name = name;
    data.objectPath = objectPath;

    data.saveData = saveData;
    data.loadData = loadData;

    data.savePosition = savePosition;
    data.saveRotation = saveRotation;
    data.saveScale = saveScale;

    Vector3 position = transform.position;
    data.position = position;

    Vector3 rotation = transform.rotation.eulerAngles;
    data.rotation = rotation;

    Vector3 scale = transform.localScale;
    data.scale = scale;
}

public void LoadData()
{
    id = data.id;

    name = data.name;
    objectPath = data.objectPath;

    saveData = data.saveData;
    loadData = data.loadData;

    savePosition = data.savePosition;
    saveRotation = data.saveRotation;
    saveScale = data.saveScale;

    if (savePosition) { transform.position = data.position; }
    if (saveRotation) { transform.eulerAngles = data.rotation; }
    if (saveScale) { transform.localScale = data.scale; }

    savedElements = data.savedElements;
}

SavedObjectData 类(简化):

//this class is in SavedObject.cs
public int id;

public string name;
public string objectPath;

public bool saveData;
public bool loadData;

public bool savePosition;
public bool saveRotation;
public bool saveScale;

public Vector3 position;
public Vector3 rotation;
public Vector3 scale;

public List<SavedElement> savedElements = new List<SavedElement>();

public string BoolToString(bool value)
{
    string result = "";

    if (value)
    {
        result = "t";
    }
    else
    {
        result = "f";
    }

    return result;
}

public bool StringToBool(string value)
{
    bool result;

    if (value == "t")
    {
        result = true;
    }
    else
    {
        result = false;
    }

    return result;
}

public void StoreValues()
{
    savedElements = new List<SavedElement>();

    savedElements.Add(new SavedElement() { name = "ID", value = id.ToString() });

    savedElements.Add(new SavedElement() { name = "Name", value = name });

    savedElements.Add(new SavedElement() { name = "Path", value = objectPath });

    savedElements.Add(new SavedElement() { name = "SD", value = BoolToString(saveData) });
    savedElements.Add(new SavedElement() { name = "LD", value = BoolToString(loadData) });

    savedElements.Add(new SavedElement() { name = "SP", value = BoolToString(savePosition) });
    savedElements.Add(new SavedElement() { name = "SR", value = BoolToString(saveRotation) });
    savedElements.Add(new SavedElement() { name = "SS", value = BoolToString(saveScale) });

    if (savePosition) { savedElements.Add(new SavedElement() { name = "P", value = "x:" + position.x + "|y:" + position.y + "|z:" + position.z }); }

    if (saveRotation) { savedElements.Add(new SavedElement() { name = "R", value = "x:" + rotation.x + "|y:" + rotation.y + "|z:" + rotation.z }); }

    if (saveScale) { savedElements.Add(new SavedElement() { name = "S", value = "x:" + scale.x + "|y:" + scale.y + "|z:" + scale.z }); }
}

public void LoadValues()
{
    foreach (SavedElement savedElement in savedElements)
    {
        string[] sectionedData;

        switch (savedElement.name)
        {
            default:
                break;

            case "ID":
                id = int.Parse(savedElement.value);
                break;

            case "Name":
                name = savedElement.value;
                break;

            case "Path":
                objectPath = savedElement.value;
                break;

            case "SD":
                saveData = StringToBool(savedElement.value);
                break;

            case "LD":
                loadData = StringToBool(savedElement.value);
                break;

            case "SP":
                savePosition = StringToBool(savedElement.value);
                break;

            case "SR":
                saveRotation = StringToBool(savedElement.value);
                break;

            case "SS":
                saveScale = StringToBool(savedElement.value);
                break;

            case "P":
                sectionedData = savedElement.value.Split('|');

                position = new Vector3(float.Parse(sectionedData[0].Split(':')[1]), float.Parse(sectionedData[1].Split(':')[1]), float.Parse(sectionedData[2].Split(':')[1]));
                break;

            case "R":
                sectionedData = savedElement.value.Split('|');

                rotation = new Vector3(float.Parse(sectionedData[0].Split(':')[1]), float.Parse(sectionedData[1].Split(':')[1]), float.Parse(sectionedData[2].Split(':')[1]));
                break;

            case "S":
                sectionedData = savedElement.value.Split('|');

                scale = new Vector3(float.Parse(sectionedData[0].Split(':')[1]), float.Parse(sectionedData[1].Split(':')[1]), float.Parse(sectionedData[2].Split(':')[1]));
                break;
        }
    }
}

SavedElement 类(简化):

//this class is in SavedObject.cs
public string name;
public string value;

SaveData 类(简化):

//this class is in SaveData.cs
private IEnumerator SaveObjects(string path, string rootElementName, string objectElementName, GameObject savedObjects){

    objects = new List<SavedObject>();

    //Compiles List
    foreach (Transform savedObject in savedObjects.transform)
    {
        if(savedObject.gameObject.GetComponent<SavedObject>() != null)
        {
            objects.add(savedObject.gameObject.GetComponent<SavedObject>())
        }
    }

    //Creates new instance of SaveSystem
    saveSystem = new SaveSystem
    {
        filepath = path,
        rootElementName = rootElementName,
        objectElementName = objectElementName,
        saveData = this,
        datasets = objects
    };

    //checks if saveSystem.datasets == objects then starts the thread to save the data
    //PLEASE NOTE: the reference is true but none of the data is updated and accessible
    saveSystem.StartSaveThread();

    while (saveSystem.threadRunning || saveSystem.syncingData)
    {
        yield return null;
    }
}

SaveSystem 类(简化):

//this class is in SaveSystem.cs
public SaveData saveData;
public string filepath;
public string rootElementName;
public string objectElementName;
public int objectsSaved;
public List<SavedObject> datasets;

Thread saveThread;

public volatile bool threadRunning = false;
public volatile bool syncingData = false;

public void SaveData()
{
    bool saveComplete = false;
    threadRunning = true;

    while (threadRunning && saveComplete == false)
    {
        objectsSaved = 0;

        XmlDocument saveFile = new XmlDocument();

        XmlElement documentRoot = saveFile.CreateElement(rootElementName);

        foreach (SavedObject dataset in datasets)
        {
            XmlElement savedObjectElement = saveFile.CreateElement(objectElementName);

            foreach (SavedElement savedValue in dataset.data.savedElements)
            {
                XmlElement newElement = saveFile.CreateElement(savedValue.name);
                newElement.InnerText = savedValue.value;

                savedObjectElement.AppendChild(newElement);
            }

            documentRoot.AppendChild(savedObjectElement);

            objectsSaved++;
        }

        saveFile.AppendChild(documentRoot);

        saveFile.Save(filepath);

        saveComplete = true;
    }

    threadRunning = false;
}

public void StartSaveThread()
{
    bool warned = false;

    //The error is here
    //The reference returns true but none of the data is accessible
    while(datasets != saveData.objects)
    {
        if(warned == false)
        {
            Debug.Log("Waiting for data to sync");
            syncingData = true;
            warned = true;
        }
    }

    syncingData = false;

    saveThread = new Thread(SaveData);
    saveThread.Start();
}

标签: c#multithreadingunity3d

解决方案


我首先让它易于重现,例如,启动线程,等待一秒钟,然后分配 List 变量,以便您可以通过让线程首先运行来不断重现竞争条件。

也许更改您的逻辑,以便在您确定已设置变量之前不要启动线程,或者您可以使用https://msdn.microsoft.com/en-us/library/system.threading。线程调用 WaitOne() 的manualresetevent(v=vs.110).aspx对象,这将阻塞该行上的线程,直到您的 List 代码路径分配该值,然后在 ManualResetEvent 上调用 Set() 事件。


推荐阅读