2013-06-19 4 views
0

У меня есть класс, который содержит метод для приема данных UDP в отдельном потоке. Я делаю это, чтобы избежать того, чтобы основное приложение (которое работает в Unity3D) не задерживалось.Обработка полученных данных UDP в другом потоке в C#

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

Вот примерно то, что UDPReceiver выглядит следующим образом:

public class UDPReciever { 

    //... 

    public UDPReciever() { 
     m_Port = 12345; 
     m_Worker = new Thread(new ThreadStart(recvData)); 
     m_Worker.IsBackground = true; 
     m_Worker.Start(); 
    } 

    void recvData() { 
     m_UDPClient = new UdpClient(m_Port); 
     while (true) { 
      try { 
       IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0); 
       byte[] data = (m_UDPClient.Receive(ref anyIP)); 

       // TODO: Hand 'data' to NetworkController class (running in the original thread) for processing 

      } catch (Exception err) { 
        print(err.ToString()); 
      } 
     } 
    } 

} 

Это примерно то, что класс NetworkController должен выглядеть. В идеале метод «OnNewData» вызывается каждый раз, когда новый пакет принимается с данными, переданными в качестве аргумента.

public class NetworkController { 

    //... 

    void OnNewData(pData) { 
     // Process the data in this thread 
    } 

} 

Как бы я мог добиться этого? Заранее спасибо.

+0

Почему не просто: ** OnNewData (данные) ** + некоторые ** Invoke **? Извините, если я что-то упустил ... Есть что-то особенное в Unity? – Pragmateek

+0

@Pragmateek Как бы я вызвал метод? Мои знания о потоках довольно ограничены. –

+0

Единственная проблема, с которой вы могли столкнуться, это обновление некоторых компонентов пользовательского интерфейса с ** привязкой к потоку **, для этого требуется специальный метод для перевода вашей обработки в очередь потоков пользовательского интерфейса, в противном случае это так же просто, как вызов метода: 'byte [ ] data = (m_UDPClient.Receive (ref anyIP)); networkController.OnNewData (data); ' – Pragmateek

ответ

1

Вот как это можно сделать (не проверено):

public class Dispatcher : MonoBehaviour 
{  
    private static readonly BlockingCollection<Action> tasks = new BlockingCollection<Action>(); 

    public static Dispatcher Instance = null; 

    static Dispatcher() 
    { 
     Instance = new Dispatcher(); 
    } 

    private Dispatcher() 
    { 
    } 

    public void InvokeLater(Action task) 
    { 
     tasks.Add(task); 
    } 

    void FixedUpdate() 
    { 
     if (tasks.Count > 0) 
     { 
      foreach (Action task in tasks.GetConsumingEnumerable()) 
      { 
       task(); 
      } 
     } 
    } 
} 
... 
NetworkController networkControllerInstance; 

void recvData() 
{ 
    m_UDPClient = new UdpClient(m_Port); 
    while (true) 
    { 
     try 
     { 
      IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0); 
      byte[] data = (m_UDPClient.Receive(ref anyIP)); 

      Dispatcher.Instance.InvokeLater(() => networkControllerInstance.OnNewData(data)); 
     } 
     catch (Exception err) 
     { 
      print(err.ToString()); 
     } 
    } 
} 

EDIT:

версия, которая должна быть совместима с .Net 3.5:

public class Dispatcher : MonoBehaviour 
{  
    private static readonly Queue<Action> tasks = new Queue<Action>(); 

    public static Dispatcher Instance = null; 

    static Dispatcher() 
    { 
     Instance = new Dispatcher(); 
    } 

    private Dispatcher() 
    { 
    } 

    public void InvokeLater(Action task) 
    { 
     lock (tasks) 
     { 
      tasks.Enqueue(task); 
     } 
    } 

    void FixedUpdate() 
    { 
     while (tasks.Count > 0) 
     { 
      Action task = null; 

      lock (tasks) 
      { 
       if (tasks.Count > 0) 
       { 
        task = tasks.Dequeue(); 
       } 
      } 

      task(); 
     } 
    } 
} 

EDIT 2:

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

void FixedUpdate() 
{ 
    if (tasks.Count != 0) 
    { 
     Action task = null; 

     lock (tasks) 
     { 
      if (tasks.Count != 0) 
      { 
       task = tasks.Dequeue(); 
      } 
     } 

     task(); 
    } 
} 
+0

Unity поддерживает только до Mono 2.6 (.net 3.5, я считаю), и из этого только подмножество. Значение «BlockingCollections» для нас нечетких пользователей Unity. – Jerdak

+0

@Jerdak: Я обновил свой ответ тем, что должно работать с вашей антикварной версией;) – Pragmateek

+0

Можете ли вы отправить мне по факсу обновление? :) Одно замечание для других пользователей: если рабочий поток назначает задачи быстрее, чем 'task()' может выполняться, этот код может в конечном итоге заблокировать основной поток Unity. – Jerdak