2016-10-19 4 views
2

유니티 에디터에서 파일 시스템 감시자를 사용할 수있는 스레드 안전 클래스가 필요합니다. 이미 스레딩이 코 루틴에서 가능하지는 않지만 스레드 스레딩이 불가능하다는 것을 알고 있습니다. 편집기에서도 허용됩니다.스레드 안전 파일 시스템 감시자 유니티 에디터

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로부터 자동으로 모든것을 다시 컴파일하고 싶다. 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

제 경험으로는 스레드를 사용할 수 있지만 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