Ошибка многопоточности

Программирование на Юнити.

Ошибка многопоточности

Сообщение Dos87 03 сен 2010, 06:16

Всем привет!

Столкнулся с ошибкой:
!Thread::EqualsCurrentThreadID(m_MainThreadID)
Ошибка происходит при загрузке AssetBundle (возможно при любой загрузке, не проверял) через WWW. Блокирование потоков не помогает:
Код: Выделить всё
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        lock (www)
        {
            if (www.error == null)
            {
                Progress(this, www.progress); //Это событие. Загрузка проходит без ошибок
                if (www.isDone) //При считывании свойства есть пара ошибок
                    Complete(this, www.assetBundle); //Это событие. Дальше просто вытягивается mainAsset, что приводит к большому числу ошибок
            }
        }
    }


Иногда эти ошибки приводят к полному зависанию программы, иногда объект появляется на сцене (частично или полностью), но ошибки остаются.
Никак не могу понять в чём может быть причина. Объект блокируется от посторонних потоков и выполняется в основном потоке(в том же, что и первый скрипт на сцене). На мой взгляд это полностью исключает влияние остальных потоков.
Читал на англоязычных форумах, что это ошибка версии 2.6, это так?
Кто нить встречался с такой проблемой? Помогите плз обойти эту ошибку.
Dos87
UNIт
 
Сообщения: 64
Зарегистрирован: 28 июл 2010, 13:46

Re: Ошибка многопоточности

Сообщение DbIMok 03 сен 2010, 10:19

эта ошибка означает, что идет обращение к объектам движка из другого потока. это свойство движка, а не ошибка версии 2.6. думаю вам нужно описать свою задачу, для того, чтобы вам смогли помочь с ее решением.
правильный вопрос - половина ответа. учитесь формулировать вопросы понятно.
Новости > _Telegram чат @unity3d_ru (11.6k/4.8k online) > _Telegram канал @unity_news (4.7k подписчиков) > Телеграм тема > "Спасибо"
Аватара пользователя
DbIMok
Адепт
 
Сообщения: 6372
Зарегистрирован: 31 июл 2009, 14:05

Re: Ошибка многопоточности

Сообщение Neodrop 03 сен 2010, 11:34

Unity не потокобезопасен. Большинство объектов и их свойств недоступны из потоков или доступ к ним и попытки манипуляции, приводят к критическим ошибкам. Работать с потоками нужно очень осторожно.
Добавить neodrop в Skype
Изображение
"Спасибо!" нашему порталу, вы сможете сказать ЗДЕСЬ.
Если проблема не решается честно, нужно её обмануть! || Per stupiditas at Astra!
Страх порождает слабость. Бесстрашных поражают пули.
Протратившись на блядях байтах, на битах не экономят.
Аватара пользователя
Neodrop
Админ
 
Сообщения: 8480
Зарегистрирован: 08 окт 2008, 15:42
Откуда: Питер
Skype: neodrop
  • Сайт

Re: Ошибка многопоточности

Сообщение Dos87 03 сен 2010, 12:06

Задача: загрузить assetBundle с известного url и показать его на сцене.
Некоторые данные предварительно уже были загружены с сервера в статическую базу данных, так что объект "map" который передаётся в метод "Play()" уже имеет все данные, в т.ч. url для загрузки assetBundle, но не имеет самого assetBundle (Instanse).

Щелкаем на кнопку, запускается игра ( Game.Play() ):
Код: Выделить всё
public class Game : MonoBehaviour
{
    private static Map currentMap;

    public static void Play(Map map)
    {
        if (!map.Loaded)
        {
            Debug.Log("Map loading is started");
            map.Complete += new Map.CompleteEvent(map_Complete);
            map.Error += new Map.ErrorEvent(map_Error);
            map.Progress += new Map.ProgressEvent(map_Progress);
            map.Load();
        }
        else
        {
            InstantiateMap(map);
        }
    }

    private static void map_Progress(Map sender, float percent)
    {
        Debug.Log("Map loading progress: " + percent);
    }
    private static void map_Error(Map sender, string errorMsg)
    {
        Debug.Log("Map loading error: " + errorMsg);
        RemoveMapListeners(sender);
    }
    private static void map_Complete(Map sender)
    {
        Debug.Log("Map is loaded");
        Debug.Log("Map id: " + sender.ID);
        Debug.Log("Map name: " + sender.Name);
        RemoveMapListeners(sender);
        InstantiateMap(sender);
    }
    private static void RemoveMapListeners(Map map)
    {
        map.Complete -= new Map.CompleteEvent(map_Complete);
        map.Error -= new Map.ErrorEvent(map_Error);
        map.Progress -= new Map.ProgressEvent(map_Progress);
    }
    private static void InstantiateMap(Map map)
    {
        Application.LoadLevel("GameScene");
        currentMap = map;
        GameObject mapInstanse = (GameObject)Instantiate(currentMap.Instanse, Vector3.zero, new Quaternion(0.0f, 0.0f, 0.0f, 0.0f));
    }
}

Этот класс не вешается на объект сцены, а просто вызываются его статические методы.

Вот класс Map:
Код: Выделить всё
public class Map
{
    public delegate void CompleteEvent(Map sender);
    public event CompleteEvent Complete;
    public delegate void ErrorEvent(Map sender, string errorMsg);
    public event ErrorEvent Error;
    public delegate void ProgressEvent(Map sender, float percent);
    public event ProgressEvent Progress;

    private int id;
    private string name;
    private string type;
    private int minLvl;
    private int completeFirst;
    private string src;
    private bool loaded;
    private GameObject instanse;
    private EventedWWW www;

    public Map(int id, string name, string type, int minLVL, int completeFirst, string src)
    {
        try
        {
            ID = id;
            Name = name;
            Type = type;
            MinLVL = minLVL;
            CompleteFirst = completeFirst;
            Src = src;
        }
        catch (Exception exc)
        {
            Debug.Log("error in the 'Map()'\n" + exc.Message);
        }
    }

    public void Load()
    {
        try
        {
            if (!Loaded)
            {
                Debug.Log("Map.Load() is started");
                www = new EventedWWW();
                Debug.Log("EventedWWW is created");
                www.Complete += new EventedWWW.CompleteEvent(www_Complete);
                www.Error += new EventedWWW.ErrorEvent(www_Error);
                www.Progress += new EventedWWW.ProgressEvent(www_Progress);
                www.Load(Src);
                Debug.Log("www.Load(Src) is started");
            }
            else Complete(this);
        }
        catch (Exception exc)
        {
            Debug.Log("error in the 'Load()'\n" + exc.Message);
        }
    }

    private void www_Progress(EventedWWW sender, float percent)
    {
        Progress(this, percent);
    }
    private void www_Error(EventedWWW sender, string errorMsg)
    {
        Error(this, errorMsg);
        RemoveWWWListeners();
    }
    private void www_Complete(EventedWWW sender, AssetBundle assetBundle)
    {
        try
        {
            Debug.Log("Map Complete"); //До этой строки лога пара ошибок
            Instanse = (GameObject)assetBundle.mainAsset; //Тут высыпается куча таких же ошибок (Обычно зависает программа)
            Loaded = true;
            Complete(this);
        }
        catch
        {
            Debug.LogError("Map.www_Complete() error!");
        }
        finally
        {
            RemoveWWWListeners();
        }
    }
    private void RemoveWWWListeners()
    {
        www.Complete -= new EventedWWW.CompleteEvent(www_Complete);
        www.Error -= new EventedWWW.ErrorEvent(www_Error);
        www.Progress -= new EventedWWW.ProgressEvent(www_Progress);
    }

    public int ID { get { return id; } private set { id = value; } }
    public string Name { get { return name; } private set { name = value; } }
    public string Type { get { return type; } private set { type = value; } }
    public int MinLVL { get { return minLvl; } private set { minLvl = value; } }
    public int CompleteFirst { get { return completeFirst; } private set { completeFirst = value; } }
    public string Src { get { return src; } private set { src = value; } } //URL for assetBundle

    public bool Loaded { get { return loaded; } private set { loaded = value; } }
    public GameObject Instanse { get { return instanse; } private set { instanse = value; } } //loaded and casted assetBundle


Класс EventedWWW:
Код: Выделить всё
public class EventedWWW
{
    public delegate void ProgressEvent(EventedWWW sender, float percent);
    public event ProgressEvent Progress;
    public delegate void CompleteEvent(EventedWWW sender, AssetBundle assetBundle);
    public event CompleteEvent Complete;
    public delegate void ErrorEvent(EventedWWW sender, string errorMsg);
    public event ErrorEvent Error;

    WWW www;
    private System.Timers.Timer timer;

    public EventedWWW()
    {
    }

    public void Load(string url)
    {
        try
        {
            Debug.LogWarning(Thread.CurrentThread.ManagedThreadId);
            timer = new System.Timers.Timer(200);
            timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
            timer.Start();
            Debug.Log("EventedWWW.timer has been created");
            www = new WWW(url);
            lock (www)
            {
                //www.threadPriority = UnityEngine.ThreadPriority.High;
                Debug.Log("EventedWWW.www has been created");
                if (www.error != null)
                    Error(this, www.error);
            }
        }
        catch (Exception exc)
        {
            Debug.LogError("EventedWWW.Load() Error! " + exc.Message);
        }
    }

    private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        lock (www)
        {
            if (www.error == null)
            {
                Progress(this, www.progress);
                if (www.isDone)
                {
                    Complete(this, www.assetBundle);
                    Clear();
                }
            }
            else
            {
                Error(this, www.error);
                Clear();
            }
        }
    }
    private void Clear()
    {
        timer.Stop();
        timer.Elapsed -= new System.Timers.ElapsedEventHandler(timer_Elapsed);
        timer.Dispose();
    }
}




На фоне всего этого открыта связь между асинхронными многопоточными клиентом и сервером, но она никак не связана с этим кодом.
Dos87
UNIт
 
Сообщения: 64
Зарегистрирован: 28 июл 2010, 13:46

Re: Ошибка многопоточности

Сообщение Neodrop 03 сен 2010, 12:10

AssetBundle.LoadAsync и используйте ваше время на что-нибудь более полезное для вашей программы.
P.S. Обратите внимание на тег syntax
Добавить neodrop в Skype
Изображение
"Спасибо!" нашему порталу, вы сможете сказать ЗДЕСЬ.
Если проблема не решается честно, нужно её обмануть! || Per stupiditas at Astra!
Страх порождает слабость. Бесстрашных поражают пули.
Протратившись на блядях байтах, на битах не экономят.
Аватара пользователя
Neodrop
Админ
 
Сообщения: 8480
Зарегистрирован: 08 окт 2008, 15:42
Откуда: Питер
Skype: neodrop
  • Сайт

Re: Ошибка многопоточности

Сообщение Dos87 04 сен 2010, 03:05

Всё равно ошибки есть, только сейчас бОльшая часть при Instantiate.
Задам кучу, на мой взгляд глупых вопросов (просто уже не знаю на что думать)
Сам класс Map был создан не в основном потоке и записан в статический словарь, после чего я к нему обращаюсь из основного потока. Это как то может влиять на ошибки?
Могут ли другие активные потоки, которые не пересекаются ни с чем (просто открытый сокет канал между клиентом и сервером. Даже не идёт передача данных в момент этих ошибок) влиять на ошибки?
Что такое основной поток с точки зрения Юнити? Меняется ли он при загрузке новой сцены?
Посоветуйте какие нить способы проверки, исправления. Уже перепробовал всё что знал.
Спс!
Dos87
UNIт
 
Сообщения: 64
Зарегистрирован: 28 июл 2010, 13:46

Re: Ошибка многопоточности

Сообщение Neodrop 04 сен 2010, 10:50

Я не понимаю вообще причины вашей упёртости. Вы пытаетесь реализовать в потоках то, что и так уже реализовано в двиге надёжно и стабильно. Это раз.
Во-вторых, после выхода 3.0 ваши проблемы с потоками увеличатся на порядок. Там ещё круче завернули гайки с потокобезопасностью и вообще почти ничего напрямую нельзя из потока не только изменить, но зачастую даже прочитать.
В третьих, если вам не понятны намёки, напишу прямо - замените теги на удобочитаемые. Скролить ваш зелёный листинг нет никакого желания.
Добавить neodrop в Skype
Изображение
"Спасибо!" нашему порталу, вы сможете сказать ЗДЕСЬ.
Если проблема не решается честно, нужно её обмануть! || Per stupiditas at Astra!
Страх порождает слабость. Бесстрашных поражают пули.
Протратившись на блядях байтах, на битах не экономят.
Аватара пользователя
Neodrop
Админ
 
Сообщения: 8480
Зарегистрирован: 08 окт 2008, 15:42
Откуда: Питер
Skype: neodrop
  • Сайт

Re: Ошибка многопоточности

Сообщение Dos87 04 сен 2010, 12:25

Причина упёртости в том, что я НЕ использую потоков при загрузке assetBundle. А так как ошибка связана с многопоточностью я говорил, что в отдельном потоке у меня работает сокет-клиент, через который была изначально загружена XML с данными (вдруг в юнити левый поток может привести к такой ошибке). В момент загрузки assetBundle socket-клиент находится в режиме прослушивания входящего сообщения. Больше многопоточность у меня нигде не используется!
Т.к. не нашел в юнити средств для отладки, пишу сюда все эти глупые вопросы. Думал решение должно было быть простым, т.к. даже C# используется, а что то типа InvokeRequired из того же C# придумать не смогли.

Короче решил не париться больше с этим косяком, на который и так уже ушел не один день. Просто полностью уберу все потоки и перепишу сокет-клиент. Пусть будет немного медленнее, зато надеюсь, будет работать.

Пока для себя сделал вывод:
Заявленная разработчиками юнити многопоточность не пригодна для программирования, т.к. нет никаких средств отладки и управления потоками. Ошибка в моей программе, как мне кажется (не смог проверить), в том, что класс Map (через него при вызове метода Load() грузится assetBundle) был создан не из базового потока, а в дальнейшем при вызове метода Load() из базового потока, юнити продолжает считать, что объект принадлежит потоку, в котором он был создан.
Другими словами: у нас есть статический объект, который был создан из второстепенного потока. Через базовый поток мы вызываем метод этого объекта, который обращается к движку. Вот тут и появляются ошибки.
Dos87
UNIт
 
Сообщения: 64
Зарегистрирован: 28 июл 2010, 13:46

Re: Ошибка многопоточности

Сообщение Neodrop 04 сен 2010, 12:30

[V] Средства отладки есть. В 3.0 можно использовать дебаггер из среды MonoDevelop
[V] Заявленная многопоточность работает без проблем, если ею корректно пользоваться.
[V] Объекты [unity 3D] Unity создавать в потоках нельзя. Ничего хорошего из этого не получится.
Добавить neodrop в Skype
Изображение
"Спасибо!" нашему порталу, вы сможете сказать ЗДЕСЬ.
Если проблема не решается честно, нужно её обмануть! || Per stupiditas at Astra!
Страх порождает слабость. Бесстрашных поражают пули.
Протратившись на блядях байтах, на битах не экономят.
Аватара пользователя
Neodrop
Админ
 
Сообщения: 8480
Зарегистрирован: 08 окт 2008, 15:42
Откуда: Питер
Skype: neodrop
  • Сайт

Re: Ошибка многопоточности

Сообщение Zaicheg 04 сен 2010, 12:31

Dos87 писал(а):Пока для себя сделал вывод:
Заявленная разработчиками юнити многопоточность не пригодна для программирования, т.к. нет никаких средств отладки и управления потоками.

Мне для расширения кругозора: где именно Вы встретили заявления разработчиков о многопоточности?
Дьяченко Роман
e-mail: _zaicheg.reg@gmail.com
skype: zaicheg12
vkontakte: _vk.com/zaichegq
Работа: _wie3.com _www.sanviz.com
Аватара пользователя
Zaicheg
Адепт
 
Сообщения: 3024
Зарегистрирован: 19 июн 2009, 15:12
Откуда: Череповец

Re: Ошибка многопоточности

Сообщение Dos87 04 сен 2010, 12:41

Ну не именно разработчики это сказали. Просто в описании юнити упоминается, что поддерживает многопоточность.
Также при сравнении юнити и флэш, многопоточность стоит примерно на 3-5м месте. Вообще много где о ней упоминается.
Dos87
UNIт
 
Сообщения: 64
Зарегистрирован: 28 июл 2010, 13:46

Re: Ошибка многопоточности

Сообщение DbIMok 04 сен 2010, 13:24

юнити поддерживает многопоточность. в потоках можно грузить файлы, делать расчеты, да много всего. так же есть внутренняя псевдомногопоточность в виде Coroutines.
правильный вопрос - половина ответа. учитесь формулировать вопросы понятно.
Новости > _Telegram чат @unity3d_ru (11.6k/4.8k online) > _Telegram канал @unity_news (4.7k подписчиков) > Телеграм тема > "Спасибо"
Аватара пользователя
DbIMok
Адепт
 
Сообщения: 6372
Зарегистрирован: 31 июл 2009, 14:05

Re: Ошибка многопоточности

Сообщение Dos87 04 сен 2010, 15:35

Не спорю - можно, но это неудобно и проблематично. Я сам с потоками работаю уже больше года (С#). Сразу было всё понятно и удобно, а с выходом .NET4.0 стало совсем просто и без лишнего кода. Думаю, что те, кто работали с потоками в языках высокого уровня меня поймут - это небо и земля по сравнению с Юнити. В Юнити многопоточность явно не доработана (еще ОЧЕНЬ много других лагов и багов, но пока говорим про потоки). На C# под .NET я распараллеливал всё, что хоть немного нуждалось в этом, в Юнити решил от этого отказаться, если нет критической необходимости. Работает она как то не стандартно. Может если в следующей версии программа не будет зависать и падать при подобных ошибках (что повысит скорость отладки), появится нормальный отладчик и будет реализовано что то похожее на Invoke и InvokeRequired в C# (для определения, в нужном ли потоке находится вызываемый оператор и для передачи управления в другой поток), то работу с потоками можно будет не избегать.
Dos87
UNIт
 
Сообщения: 64
Зарегистрирован: 28 июл 2010, 13:46

Re: Ошибка многопоточности

Сообщение Paul Siberdt 04 сен 2010, 15:38

еще ОЧЕНЬ много других лагов и багов, но пока говорим про потоки

А теперь давайте мешки ворочать. Ответьте за свое максималистическое и неудовлетворенное ОЧЕНЬ несколькими примерами лагов и багов.
Аватара пользователя
Paul Siberdt
Адепт
 
Сообщения: 5317
Зарегистрирован: 20 июн 2009, 21:24
Откуда: Moscow, Russia
Skype: siberdt
  • Сайт

Re: Ошибка многопоточности

Сообщение Neodrop 04 сен 2010, 15:53

Dos87 писал(а):Не спорю - можно, но это неудобно и проблематично. Я сам с потоками работаю уже больше года (С#). Сразу было всё понятно и удобно, а с выходом .NET4.0 стало совсем просто и без лишнего кода. Думаю, что те, кто работали с потоками в языках высокого уровня меня поймут - это небо и земля по сравнению с Юнити. В Юнити многопоточность явно не доработана (еще ОЧЕНЬ много других лагов и багов, но пока говорим про потоки). На C# под .NET я распараллеливал всё, что хоть немного нуждалось в этом, в Юнити решил от этого отказаться, если нет критической необходимости. Работает она как то не стандартно. Может если в следующей версии программа не будет зависать и падать при подобных ошибках (что повысит скорость отладки), появится нормальный отладчик и будет реализовано что то похожее на Invoke и InvokeRequired в C# (для определения, в нужном ли потоке находится вызываемый оператор и для передачи управления в другой поток), то работу с потоками можно будет не избегать.


Я работаю с потоками 12 лет. У меня совершенно иное отношение к этому вопросу в [unity 3D] Unity чем у вас :D
То, что у вас что-то не получилось с первого наскока и при поверхностном знании среды, совсем не означает, что плоха среда. Может, стоит немного поднабраться терпения и не торопиться с выводами? :-?
З.Ы. Про "лаги и баги" тоже очень понравилось. Есть нубы и есть злобные нубы. Вторые особенно интересны.

З.Ы.Ы. Я не хочу вас обидеть, но ваше отношение к задаче указывает на недостаточный опыт в освоении незнакомых технологий.

В потоках стоит работать с классами, не манипулирующими Unity-объектами. Тогда будет у вас полная ясность и лучезарное счастье. И никаких проблем, если сами не налажаете. И не стоит забывать закрывать потоки за собой, что у вас не реализовано.
Добавить neodrop в Skype
Изображение
"Спасибо!" нашему порталу, вы сможете сказать ЗДЕСЬ.
Если проблема не решается честно, нужно её обмануть! || Per stupiditas at Astra!
Страх порождает слабость. Бесстрашных поражают пули.
Протратившись на блядях байтах, на битах не экономят.
Аватара пользователя
Neodrop
Админ
 
Сообщения: 8480
Зарегистрирован: 08 окт 2008, 15:42
Откуда: Питер
Skype: neodrop
  • Сайт


Вернуться в Скрипты

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 11