2016-10-19 8 views
2

ファイルシステムウォッチャーがUnity Editorで使用するスレッドセーフなクラスが必要ですが、私は既にスレッド化がコルーチンからは不可能であることを知っていますが、エディタでも許可されています。スレッドセーフファイルシステムウォッチャーUnityエディター

だから、私の誤差があります:

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

のための1つを必要とするこれは私のコードです:

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から自動的にすべてを再コンパイルしたいDLLのプロジェクトからは変更されましたが、スレッドのためにそれを達成できません。何ができるのですか? Unityと互換性のあるファイルをリッスンする方法はありますか?

ありがとうございました。

答えて

1

私は@ケイの助けを借りて解決しました。

私はより一般的な答えを出したいので、私が望むものを達成するために自分のクラスを作ることに決めました。そして、これが結果です:

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

私の経験から、スレッドを使用できますが、ユニティクラスへのアクセスはメインスレッドからのみ実行する必要があります。私の提案は、あなたのウォッチドッグが警告するたびにメインスレッドに制御を渡すことです。

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

関連する問題