2016-10-19 5 views
2

Мне нужен потокобезопасный класс для файловой системы Watcher для использования в Unity Editor, я уже знаю, что Threading невозможно из сопрограмм, но я не знал, что нить не была разрешено также в редакторе.Thread Safe File System Watcher для Unity Editor

Итак, есть моя ошибка:

get_isEditor can only be called from the main thread. Constructors and field initializers will be executed from the loading thread when loading a scene. Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function. 0x0000000140E431ED (Unity) StackWalker::GetCurrentCallstack 0x0000000140E44EE1 (Unity) StackWalker::ShowCallstack 0x00000001405FC603 (Unity) GetStacktrace 0x00000001405F97FE (Unity) DebugStringToFile 0x00000001405F9C5C (Unity) DebugStringToFile 0x000000014035F7B3 (Unity) ThreadAndSerializationSafeCheckReportError 0x0000000140E7B988 (Unity) Application_Get_Custom_PropIsEditor 0x0000000015AC46AA (Mono JIT Code) (wrapper managed-to-native) UnityEngine.Application:get_isEditor() 0x0000000015AC42FE (Mono JIT Code) [Helpers.cs:585] Lerp2API.DebugHandler.Debug:Log (object) 0x0000000015AC41C2 (Mono JIT Code) [Helpers.cs:578] Lerp2API.DebugHandler.Debug:Log (string) 0x0000000015AC40F7 (Mono JIT Code) [LerpedEditorCore.cs:101] Lerp2APIEditor.LerpedEditorCore:Recompile (object,System.IO.FileSystemEventArgs) 0x0000000015AC3F2D (Mono JIT Code) (wrapper runtime-invoke) :runtime_invoke_void__this___object_object (object,intptr,intptr,intptr) 0x00007FFB400A519B (mono) [mini.c:4937] mono_jit_runtime_invoke 0x00007FFB3FFF84FD (mono) [object.c:2623] mono_runtime_invoke 0x00007FFB3FFFE8F7 (mono) [object.c:3827] mono_runtime_invoke_array 0x00007FFB3FFFEBCC (mono) [object.c:5457] mono_message_invoke 0x00007FFB4001EB8B (mono) [threadpool.c:1019] mono_async_invoke 0x00007FFB4001F5E2 (mono) [threadpool.c:1455] async_invoke_thread 0x00007FFB4002329F (mono) [threads.c:685] start_wrapper 0x00007FFB400D78C9 (mono) [win32_threads.c:599] thread_start 0x00007FFB77FC8364 (KERNEL32) BaseThreadInitThunk

Я скопировал полный трассировки стека, чтобы сделать осведомленный любой помощник, где может быть проблема. Потому что, я искал решение, как и любой резьбовые безопасный FWS, и да, есть один, но только для .NET 4, и мне нужно для .NET 2

Это мой код:

using System.IO; //class, namespace, redundant info... 

private static FileSystemWatcher m_Watcher; 

[InitializeOnLoadMethod] 
static void HookWatcher() 
{ 
    m_Watcher = new FileSystemWatcher("path", "*.cs"); 
    m_Watcher.NotifyFilter = NotifyFilters.LastWrite; 
    m_Watcher.IncludeSubdirectories = true; 
    //m_Watcher.Created += new FileSystemEventHandler(); //Add to the solution before compile 
    //m_Watcher.Renamed += new FileSystemEventHandler(); //Rename to the solution before compile 
    //m_Watcher.Deleted += new FileSystemEventHandler(); //Remove to the solution before compile 
    m_Watcher.Changed += Recompile; 
    m_Watcher.EnableRaisingEvents = true; 
} 

private static void Recompile(object sender, FileSystemEventArgs e) 
{ 
    Debug.Log("Origin files has been changed!"); 
} 
не

Там ничего особенного, как вы можете видеть ...

FSW я увидел это: https://gist.githubusercontent.com/bradsjm/2c839912294d0e2c008a/raw/c4a5c3d920ab46fdaa53b0e111e0d1204b1fe903/FileSystemWatcher.cs

Моя цель с этим просто, у меня есть отделенной DLL от моего текущего проекта Unity, идея проста , Я хочу автоматически перекомпилировать все из Unity, когда любое изменение из проекта DLL изменяется, но я не могу добиться этого из-за потоков, так что я могу сделать? Есть ли альтернатива, которая поддерживает файлы, совместимые с Unity?

Спасибо.

ответ

1

Я решил это с помощью @Kay, спасибо @Kay!

Я хотел сделать более общий ответ, поэтому я решил сделать свой собственный класс для достижения того, чего хотел. И это результат:

using System; 
using System.IO; 
using System.Collections.Generic; 

namespace Lerp2APIEditor.Utility 
{ 
    public class LerpedThread<T> 
    { 
     public T value = default(T); 
     public bool isCalled = false; 
     public string methodCalled = ""; 
     public Dictionary<string, Action> matchedMethods = new Dictionary<string, Action>(); 

     public FileSystemWatcher FSW 
     { 
      get 
      { 
       return (FileSystemWatcher)(object)value; 
      } 
     } 
     public LerpedThread(string name, FSWParams pars) 
     { 
      if(typeof(T) == typeof(FileSystemWatcher)) 
      { 
       FileSystemWatcher watcher = new FileSystemWatcher(pars.path, pars.filter); 

       watcher.NotifyFilter = pars.notifiers; 
       watcher.IncludeSubdirectories = pars.includeSubfolders; 

       watcher.Changed += new FileSystemEventHandler(OnChanged); 
       watcher.Created += new FileSystemEventHandler(OnCreated); 
       watcher.Deleted += new FileSystemEventHandler(OnDeleted); 
       watcher.Renamed += new RenamedEventHandler(OnRenamed); 

       ApplyChanges(watcher); 
      } 
     } 
     private void OnChanged(object source, FileSystemEventArgs e) 
     { 
      methodCalled = "OnChanged"; 
      isCalled = true; 
     } 
     private void OnCreated(object source, FileSystemEventArgs e) 
     { 
      methodCalled = "OnCreated"; 
      isCalled = true; 
     } 
     private void OnDeleted(object source, FileSystemEventArgs e) 
     { 
      methodCalled = "OnDeleted"; 
      isCalled = true; 
     } 
     private void OnRenamed(object source, RenamedEventArgs e) 
     { 
      methodCalled = "OnRenamed"; 
      isCalled = true; 
     } 
     public void StartFSW() 
     { 
      FSW.EnableRaisingEvents = true; 
     } 
     public void CancelFSW() 
     { 
      FSW.EnableRaisingEvents = false; 
     } 
     public void ApplyChanges<T1>(T1 obj) 
     { 
      value = (T)(object)obj; 
     } 
    } 
    public class FSWParams 
    { 
     public string path, 
         filter; 
     public NotifyFilters notifiers; 
     public bool includeSubfolders; 
     public FSWParams(string p, string f, NotifyFilters nf, bool isf) 
     { 
      path = p; 
      filter = f; 
      notifiers = nf; 
      includeSubfolders = isf; 
     } 
    } 
} 

Главный код класса:

namespace Lerp2APIEditor 
{ 
    public class LerpedEditorCore 
    { 

     private static LerpedThread<FileSystemWatcher> m_Watcher; 

     [InitializeOnLoadMethod] 
     static void HookWatchers() 
     { 
      EditorApplication.update += OnEditorApplicationUpdate; 

      m_Watcher.matchedMethods.Add("OnChanged",() => { 
       Debug.Log("Origin files has been changed!"); 
      }); 

      m_Watcher.StartFSW(); 
     } 

     static void OnEditorApplicationUpdate() 
     { 
      if(EditorApplication.timeSinceStartup > nextSeek) 
      { 
       if (m_Watcher.isCalled) 
       { 
        foreach (KeyValuePair<string, Action> kv in m_Watcher.matchedMethods) 
         if (m_Watcher.methodCalled == kv.Key) 
          kv.Value(); 
        m_Watcher.isCalled = false; 
       } 
       nextSeek = EditorApplication.timeSinceStartup + threadSeek; 
      } 
     } 
    } 
} 

То, что я сделал это очень просто. Я создал только общий класс, создающий экземпляр FSW или все, что вы хотите слушать. Один раз созданный, я присоединяю события, которые активируют только bool @Kay, предлагаемый мне для использования, а также метод, призванный точно знать, какой метод был вызван.

Позже в главном классе foreach циклически выбирает каждый метод, указанный каждую секунду, если обнаружено изменение, и вызван метод, связанный со строкой.

+0

Я думаю, что более суровым способом было бы назначить специально созданный SynchronizingObject. – aeroson

2

Из моего опыта вы можете использовать потоки, но вам нужно позаботиться о том, чтобы доступ к классам Unity выполнялся только из основного потока. Мое предложение состоит в том, чтобы передать управление основной теме всякий раз, когда вы предупреждаете о своем контролере.

static bool _triggerRecompile = false; 

[InitializeOnLoadMethod] 
static void HookWatcher() 
{ 
    m_Watcher = new FileSystemWatcher("path", "*.cs"); 
    // .... 
    m_Watcher.Changed += Recompile; 
    EditorApplication.update += OnEditorApplicationUpdate; 
} 

private static void Recompile(object sender, FileSystemEventArgs e) 
{ 
    bool _triggerRecompile = true; 
    // Never call any Unity classes as we are not in the main thread 
} 

static void OnEditorApplicationUpdate() 
{ 
    // note that this is called very often (100/sec) 
    if (_triggerRecompile) 
    { 
     _triggerRecompile = false; 
     Debug.Log("Origin files has been changed!"); 
     DoRecompile(); 
    } 
} 

Опрос, конечно, отвратительный и уродливый. В целом я предпочитаю основанные на событиях подходы. Но в этом специальном случае я не вижу возможности обмануть правило основной нити.

+0

Итак, проблема в вызове методов из Unity в другом потоке не? Я должен проверить, но спасибо! – z3nth10n