I have a project that without usign any form/button or nothing like that, connects with a Websocket and using async methods receives some message(on a form created by myself) that is supposed to appear on the top-right corner of the screen.
But this message can appear from time to time (2 or 3 minutes) on the screen if the websocket doesn't say that it must stop. And this message can be big enough, that in order to make it look better I make my message appear in more than one form.
It causes an impression that it's a notification. So my class that connects with the websocket and receives the message async, calls another class using a thread that is a controller. The purpose of the controller is from time to time, show that message in various new form() notifications and obviously don't do it if the websocket doesn't return any message.
But when i call the form.show the program stops working.
I've looked around stackoverflow already, but the ideas that i've found didn't seem to work.
Some say that I should use invoke, but it kept giving error saying that
"Invoke or BeginInvoke cannot be called on a control until the window handle has been created", tried to solve like this: C# calling form.show() from another thread but it didn't work.
Some said that I should use .showDialog instead of .show, but it doesn't appear to be good, because it waits the window to be closed to terminate the method and as I said I need to open more than one notification at the same time.
Some said that the form was open with .show, but it was open for a very little period of time. But i couldn't notice if that was the case and even if it was i couldn't solve it. Well, what matter is that i'm stuck and i don't know what to do more.
Edited with Code:
//Main
Application.Run(new SocketService());
//SocketService class
public SocketService()
{
alerta = null;
while (true)
{
try
{
//Console.WriteLine("Nome do UsĂșario:" + Environment.UserName);
Thread.Sleep(2000);
Connect("ws://192.168.120.38:9091").Wait();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
public static async Task Connect(string uri)
{
ClientWebSocket webSocket = null;
try
{
webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(uri), CancellationToken.None);
await Login(webSocket);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (webSocket != null)
webSocket.Dispose();
lock (consoleLock)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("WebSocket closed.");
Console.ResetColor();
}
}
}
private static async Task Login(ClientWebSocket webSocket)
{
ArraySegment<Byte> buffer = new ArraySegment<byte>(encoder.GetBytes( "{\"event\":\"loginBrowser\",\"data\":{\"login\":\"000000003077\",\"data\":\"1\"}}"));
await webSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
if (webSocket.State == WebSocketState.Open)
{
if (ShowMessage.created != true)
{
var dummy = new Control(); // to initialize SynchronizationContext
_sync = SynchronizationContext.Current;
new Thread(ThreadProc).Start();
}
await Receive(webSocket);
}
}
private static async Task Receive(ClientWebSocket webSocket)
{
while (webSocket.State == WebSocketState.Open)
{
ArraySegment<Byte> buffer = new ArraySegment<byte>(new Byte[256]);
var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}
else
{
if (result.EndOfMessage)
{
message += encoder.GetString(buffer.ToArray());
SendMessage(message);
}
else
{
message += encoder.GetString(buffer.ToArray());
}
}
}
}
public static void ShowFormFromAnotherThread(string text)
{
_sync.Post(SendOrPostCallback, text);
}
private static void SendOrPostCallback(object state)
{
var form = new Notification();
form.Text = (string)state;
form.Show();
}
private static void ThreadProc()
{
while (true)
{
Thread.Sleep(2000); // wait imitation
ShowFormFromAnotherThread("HI");
}
}
/*Notification is my form and depending on where I put this part:
var dummy = new Control(); // to initialize SynchronizationContext
_sync = SynchronizationContext.Current;
new Thread(ThreadProc).Start();
Or i doesn't call login or doesn't enter receive() method or the best case It receives the information
calls the threadProc and the ShowFormFromAnotherThread but doesn't enter SednOrPostCallBack*/
using System.Threading;
using System.Windows.Forms;
namespace ConsoleThreadSync
{
internal class Program
{
private static void Main(string[] args)
{
Application.Run(new App());
}
}
public class App : ApplicationContext
{
private readonly SynchronizationContext _sync;
public App()
{
var dummy = new Control(); // to initialize SynchronizationContext
_sync = SynchronizationContext.Current;
new Thread(ThreadProc).Start();
}
public void ShowFormFromAnotherThread(string text)
{
_sync.Post(SendOrPostCallback, text);
}
private void SendOrPostCallback(object state)
{
var form = new Form1();
form.Text = (string)state;
form.Show();
}
private void ThreadProc()
{
while (true)
{
Thread.Sleep(2000); // wait imitation
ShowFormFromAnotherThread("HI");
}
}
}
}
Try to call this:
var dummy = new Control(); // to initialize SynchronizationContext
_sync = SynchronizationContext.Current;
from a contructor SocketService() and not from async methods. This is an initialization code and it must call from main thread.
Okay, after reading a little bit more, the solution that kind worked out was this one, but the only way of using the
.show from the notifician is to use the Application.DoEvents and I've been warned from the sources that I've looked into
that this method should not be used, unless is the only option, because It can cause some problems with the Threads and other things.
So unless someone can give me another hint or clue about what to do, I have two options or use this method and try to fix some other bug
that It can cause or use the .showDialog because don't know why It works without any other problem, but to use .showDialog I've
to use another thread where I create and show the notification because if I don't do, the loop will stop at each iteration
in order to wait the .showDialog be closed. And as it isn't a problem I want to avoid using a lot of threads, because it can cause
another problem with the sync between them:
namespace ReiDoCSharp
{
class ShowMessage
{
private static RootObject alerta;
public static bool created;
private static int startPosition;
public static void setStartPosition(int start)
{
if (start < startPosition)
{
startPosition = start;
}
}
public RootObject getAlerta()
{
return ShowMessage.alerta;
}
public void setAlerta(RootObject root)
{
ShowMessage.alerta = root;
}
private static void DoWork()
{
while (true)
{
if (created != true)
{
created = true;
}
if (alerta != null)
{
string mensagem = "";
if ((alerta.data.Informacoes[1] != "") && (alerta.data.Informacoes[1] != null))
{
mensagem += alerta.data.Informacoes[1];
}
if ((alerta.data.Informacoes[0] != "") && (alerta.data.Informacoes[0] != null))
{
mensagem += alerta.data.Informacoes[0];
}
if (mensagem != "")
{
startPosition = 5;
string[] messages = mensagem.Split(new[] { "<br><br>" }, StringSplitOptions.None);
foreach (string message in messages)
{
Notification popup = new Notification();
popup.label1.Text = message;
popup.TopMost = true;
popup.Show();
Application.DoEvents();
/*Solution with the ShowDialog would be:
Task.Run(() => showNotification(message));
*/
}
}
}
Thread.Sleep(5000);
}
}
//Then I won't need to use Application.DoEvents, but would have to create more threads
private static Task showNotification(string message)
{
Notification popup = new Notification();
popup.label1.Text = message;
popup.TopMost = true;
popup.ShowDialog();
}
public static Task createPopupsAsync()
{
Task.Run(() => DoWork());
}
}
}
namespace ReiDoCSharp
{
class SocketService
{
private static object consoleLock = new object();
private const bool verbose = true;
private static readonly TimeSpan delay = TimeSpan.FromMilliseconds(3000);
private static UTF8Encoding encoder = new UTF8Encoding();
private static string message;
private static RootObject alerta;
public SocketService()
{
Begin();
}
public static void Begin()
{
alerta = null;
while (true)
{
try
{
Thread.Sleep(2000);
Connect("ws://192.168.120.38:9091").Wait();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
public static async Task Connect(string uri)
{
ClientWebSocket webSocket = null;
try
{
webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(uri), CancellationToken.None);
await Login(webSocket);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (webSocket != null)
webSocket.Dispose();
lock (consoleLock)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("WebSocket closed.");
Console.ResetColor();
}
}
}
private static async Task Login(ClientWebSocket webSocket)
{
ArraySegment<Byte> buffer = new ArraySegment<byte>(encoder.GetBytes("{\"event\":\"loginBrowser\",\"data\":{\"OPERADOR\":\"000000003077\",\"NRORG\":\"1\"}}"));
await webSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
if (webSocket.State == WebSocketState.Open)
{
Task.Factory.StartNew(() => ShowMessage.createPopupsAsync());
await Receive(webSocket);
}
}
private static async Task Receive(ClientWebSocket webSocket)
{
while (webSocket.State == WebSocketState.Open)
{
ArraySegment<Byte> buffer = new ArraySegment<byte>(new Byte[256]);
var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}
else
{
if (result.EndOfMessage)
{
message += encoder.GetString(buffer.ToArray());
SendMessage(message);
}
else
{
message += encoder.GetString(buffer.ToArray());
}
}
}
}
private static void LogStatus(bool receiving, byte[] buffer, int length, string assunto)
{
lock (consoleLock)
{
Console.ForegroundColor = receiving ? ConsoleColor.Green : ConsoleColor.Yellow;
if (verbose)
{
Console.WriteLine(encoder.GetString(buffer) + " " + assunto);
}
Console.ResetColor();
}
}
private static void SendMessage(string message)
{
message = message.Replace("event", "evento");
message = message.Replace("\0", "");
JavaScriptSerializer js = new JavaScriptSerializer();
RootObject mess = js.Deserialize<RootObject>(message);
if (mess.data.Informacoes[1] != "")
{
mess.data.Informacoes[1] += "<br>";
}
if (alerta == null)
{
alerta = mess;
}
else
{
if ((mess.data.Quantidade[0] != 0) && (mess.data.Quantidade == null))
{
if ((mess.data.Quantidade[0] == -1) && (mess.data.Informacoes[0] == ""))
{
alerta = null;
}
else
{
alerta = mess;
}
}
else if (mess.data.Quantidade[0] == 0)
{
alerta = null;
}
if ((mess.data.Quantidade[1] != 0) && (mess.data.Informacoes[1] != ""))
{
alerta = mess;
}
}
new ShowMessage().setAlerta(alerta);
message = "";
}
}
}
Related
Hi,
I'm trying to write a UDP-client that listens to a port, and then display the incoming data in a textbox.
I have written a class; UDP_Receive() that starts a backgroundworker that listens to a specified port and receives the data. The code is based on this question; C# .Net receiving UDp packets in separater thread and application exit
I think I have solved the issues with the blocking .receive() by following the accepted answer in this question; How can I safely terminate UdpClient.receive() in case of timeout?
My question is how do I get the data I receive in the backgroundworkerthread (that I created in my class) back to the mainthread, so that I can display it in a textbox?
I thought of using Invoke, but I don't have any references to the textbox in my class.
textBox_UdpPositionInData.Invoke(new EventHandler(delegate
{
textBox_UdpPositionInData.Text = receivedData;
}));
I found a very similar question about a tcp-server, but I can't see how to apply that solution here Send data from a background thread to the main thread
Any thoughts or suggestions are of course much appreciated!
Many Thanks!
internal class UDP_Receive
{
private int _portToListen = 2003;
private volatile bool listening;
BackgroundWorker _backgroundWorker;
//Constructor
public UDP_Receive()
{
Debug.WriteLine("Constructor: UDP_Receive");
this.listening = false;
}
public void StartListener()
{
if ( (_backgroundWorker==null) || (!_backgroundWorker.IsBusy))
{
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.DoWork += listenForUDPPackages_DoWork;
_backgroundWorker.RunWorkerCompleted +=listenForUDPPackages_RunWorkerCompleted;
_backgroundWorker.WorkerSupportsCancellation = true;
_backgroundWorker.RunWorkerAsync();
Debug.WriteLine("Creates a new thread: " + _backgroundWorker.ToString() );
// We are listening
this.listening = true;
}
}
public void StopListener()
{
// The user cancelled the UDP Port listening
this.listening = false;
// Cancel the backgroundworker
_backgroundWorker.CancelAsync();
// Debug
Debug.WriteLine("Stops current thread: " + _backgroundWorker.ToString());
}
public bool IsListening
{
get { return this.listening; }
}
public int PortToListen
{
get { return this._portToListen; }
set { this._portToListen = value; }
}
private void listenForUDPPackages_DoWork(object sender, DoWorkEventArgs ev)
{
UdpClient? listener = null;
try
{
listener = new UdpClient(_portToListen);
}
catch (SocketException)
{
//do nothing
}
if (listener != null)
{
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, _portToListen);
try
{
while (this.listening)
{
Debug.WriteLine("Waiting for UDP broadcast to port " + _portToListen);
byte[] receivedBytes = new byte[1024];
string receivedData;
bool timeTracker = TrackFunction(TimeSpan.FromSeconds(2), () =>
{
receivedBytes = listener.Receive(ref groupEP);
});
Debug.WriteLine("Timetracker result: " + timeTracker.ToString());
if (receivedBytes == null || receivedBytes.Length == 0)
{
// We did not recieve any data
Debug.WriteLine("No data received befor Time out ");
}
else
{
// We managed to receive some data!
// No we want to process the data and then send the result to the Thread that initiated the class.
receivedData = Encoding.Default.GetString(receivedBytes);
Debug.WriteLine("Data received: " + receivedData);
}
}
catch (Exception e)
{
Debug.WriteLine("Exception: " + e.ToString());
}
finally
{
listener.Close();
Debug.WriteLine("Finally: Done listening for UDP broadcast");
}
}
else
{
Debug.WriteLine("Error: UdpClient(_portToListen) returned null");
}
}
private void listenForUDPPackages_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e == null || e.Result == null)
{
Debug.WriteLine("listenForUDPPackages_RunWorkerCompleted e = null");
}
else
{
if (e.Cancelled)
{
Debug.WriteLine("Operation was canceled");
}
else if (e.Error != null)
{
Debug.WriteLine("Error: " + e.Error.Message);
}
else
{
Debug.WriteLine("Result: " + e.Result.ToString());
}
}
}
private static bool TrackFunction(TimeSpan timeSpan, Action codeBlock)
{
try
{
Task task = Task.Factory.StartNew(() => codeBlock());
task.Wait(timeSpan);
return task.IsCompleted;
}
catch (AggregateException ae)
{
throw ae.InnerExceptions[0];
}
}
}
You can write this kind of code to keep the UI separate from the UDP_Receive class:
internal class UDP_Receive
{
private Action<string> _invoke;
public UDP_Receive(Action<string> invoke)
{
_invoke = invoke;
}
public void Foo()
{
_invoke("Hello");
}
}
When you declare the class you then do it like this:
var ur = new UDP_Receive(t => textBox.Invoke(() => textBox.Text = t));
Now it's just a matter of calling ur.Foo() (in my example) to update the UI.
I'm having an issue where I want to be able to modify a textbox from another class.
I've tried searching and testing solutions, none seemed to do the job. (Invoking for an example..)
Code from class cThread:
class cThread
{
public bool closed = false;
private TcpClient client;
private StreamReader ins;
private StreamWriter ots;
Form1 meow = new Form1();
public cThread(TcpClient client, StreamReader ins, StreamWriter ots)
{
this.client = client;
this.ins = ins;
this.ots = ots;
}
public void run()
{
try
{
string responseLine;
responseLine = meow.bunifuCustomTextbox2.Text;
while ((responseLine = ins.ReadLine()) != null)
{
Console.WriteLine(responseLine);
meow.bunifuCustomTextbox3.Text = responseLine + " test";
if (responseLine.IndexOf("*** Adios") != -1)
{
break;
}
}
closed = true;
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
Environment.Exit(0);
}
}
This causes an error: Cross-Thread operation not valid: accessed from a thread other than the thread it was created on.
I have also tried this solution:
public void run()
{
try
{
string responseLine;
meow.bunifuCustomTextbox2.Invoke(new MethodInvoker(delegate { responseLine = meow.bunifuCustomTextbox2.Text; }));
while ((responseLine = ins.ReadLine()) != null)
{
Console.WriteLine(responseLine);
meow.bunifuCustomTextbox3.Invoke(new MethodInvoker(delegate { meow.bunifuCustomTextbox3.Text = meow.bunifuCustomTextbox2.Text; }));
meow.bunifuCustomTextbox3.Text = responseLine + " test";
if (responseLine.IndexOf("*** Adios") != -1)
{
break;
}
}
closed = true;
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
Environment.Exit(0);
}
}
This did not work either. Same error.
What I'm expecting to happen is when the user types in a message in bunifuCustomTextbox2, it'll then be set to responseLine which I want to lively be updated by bunifuCustomTextbox3.
Because this is going to be a multiplayer chat and I'm converting this code from a consoleapp to winforms..
sorry if im being a dumb :(
When you access an UI resource from another thread you should have the Dispatcher thread handle that:
Dispatcher.Invoke(() => { /* UI changes */ });
I have written a simple Producer-Consumer queue. It keeps throwing an error that the safe handle is disposed.
namespace ConsoleApplication1
{
public class ProducerConsumer:IDisposable
{
private Queue<string> tasks = new Queue<string>();
Thread work;
readonly object obj=new object();
EventWaitHandle wh = new AutoResetEvent(true);
public ProducerConsumer()
{
work = new Thread(doWork);
work.Start();
}
public void doWork()
{
while (true)
{
string task = null;
wh.WaitOne();
lock (obj){
if (tasks.Count > 0)
{
task = tasks.Dequeue();
if (task == null) return;
else
{
Console.WriteLine("Performing task: " + task);
Thread.Sleep(1000); // simulate work...
}
}
}
}
}
public void ShutDown()
{
tasks.Enqueue(null);
wh.Close();
work.Join();
}
public void Dispose()
{
tasks.Enqueue(null);
wh.Close();
work.Join();
}
public void AddTask(string str)
{
lock(obj){tasks.Enqueue(str);wh.Set();}
}
}
class Program
{
static EventWaitHandle _waitHandle = new AutoResetEvent(false);
static void Main(string[] args)
{
ProducerConsumer prod = new ProducerConsumer();
prod.AddTask("Started");
for (int i = 0; i < 5; i++) prod.AddTask(Convert.ToString(i));
prod.AddTask("Done");
prod.ShutDown();
Console.ReadLine();
}
}
}
If I change the wh.WaitOne() to the else part then it starts working
public void doWork()
{
while (true)
{
string task = null;
lock (obj){
if (tasks.Count > 0)
{
task = tasks.Dequeue();
if (task == null) return;
else
{
Console.WriteLine("Performing task: " + task);
Thread.Sleep(1000); // simulate work...
}
}
else
wh.WaitOne();
}
}
}
Can somebody throw a light on why this is happening?
This question already has answers here:
A pattern to pause/resume an async task?
(5 answers)
Closed 5 years ago.
I have a sample code, taken from MSDN, to which I would like implement the Pause / Resume functionality within while loop. Could anybody propose a solution / parern which would hande this?
private async void startButton_Click(object sender, RoutedEventArgs e)
{
resultsTextBox.Clear();
// Instantiate the CancellationTokenSource.
cts = new CancellationTokenSource();
try
{
await AccessTheWebAsync(cts.Token);
resultsTextBox.Text += "\r\nDownloads complete.";
}
catch (OperationCanceledException)
{
resultsTextBox.Text += "\r\nDownloads canceled.\r\n";
}
catch (Exception)
{
resultsTextBox.Text += "\r\nDownloads failed.\r\n";
}
cts = null;
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
}
async Task AccessTheWebAsync(CancellationToken ct)
{
HttpClient client = new HttpClient();
// Make a list of web addresses.
List<string> urlList = SetUpURLList();
// ***Create a query that, when executed, returns a collection of tasks.
IEnumerable<Task<int>> downloadTasksQuery =
from url in urlList select ProcessURL(url, client, ct);
// ***Use ToList to execute the query and start the tasks.
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
// ***Add a loop to process the tasks one at a time until none remain.
while (downloadTasks.Count > 0)
{
// Identify the first task that completes.
Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);
// ***Remove the selected task from the list so that you don't
// process it more than once.
downloadTasks.Remove(firstFinishedTask);
// Await the completed task.
int length = await firstFinishedTask;
resultsTextBox.Text += String.Format("\r\nLength of the download: {0}", length);
}
}
There is an MSDN article that solves this using a PauseToken (similar to a CancellationToken).
Here's the sample code from that article that demonstrates this concept:
namespace PauseTokenTestApp
{
public class PauseTokenSource
{
private volatile TaskCompletionSource<bool> m_paused;
internal static readonly Task s_completedTask = Task.FromResult(true);
public bool IsPaused
{
get { return m_paused != null; }
set
{
if (value)
{
Interlocked.CompareExchange(
ref m_paused, new TaskCompletionSource<bool>(), null);
}
else
{
while (true)
{
var tcs = m_paused;
if (tcs == null) return;
if (Interlocked.CompareExchange(ref m_paused, null, tcs) == tcs)
{
tcs.SetResult(true);
break;
}
}
}
}
}
public PauseToken Token { get { return new PauseToken(this); } }
internal Task WaitWhilePausedAsync()
{
var cur = m_paused;
return cur != null ? cur.Task : s_completedTask;
}
}
public struct PauseToken
{
private readonly PauseTokenSource m_source;
internal PauseToken(PauseTokenSource source) { m_source = source; }
public bool IsPaused { get { return m_source != null && m_source.IsPaused; } }
public Task WaitWhilePausedAsync()
{
return IsPaused ?
m_source.WaitWhilePausedAsync() :
PauseTokenSource.s_completedTask;
}
}
class Program
{
static void Main()
{
var pts = new PauseTokenSource();
Task.Run(() =>
{
while (true)
{
Console.ReadLine();
pts.IsPaused = !pts.IsPaused;
}
});
SomeMethodAsync(pts.Token).Wait();
}
public static async Task SomeMethodAsync(PauseToken pause)
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine(i);
await Task.Delay(100);
await pause.WaitWhilePausedAsync();
}
}
}
}
I have a class which contains a method for receiving UDP data in a separate thread. I do this to avoid the main application (which is running in Unity3D) from stalling.
I need to pass the data that is received in the separate thread to another class, which runs on the original thread and, as such, is able to interact with Unity3D.
Here is roughly what the UDPReceiver looks like:
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());
}
}
}
}
This is roughly what the NetworkController class needs to look like. Ideally the "OnNewData" method would be called every time a new packet is received with the data passed as an argument.
public class NetworkController {
//...
void OnNewData(pData) {
// Process the data in this thread
}
}
How would I go about achieving this? Thanks in advance.
Here is how it could be done (not tested):
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:
A version that should be compliant with .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:
if you want to avoid freezing the main thread during a too long period:
void FixedUpdate()
{
if (tasks.Count != 0)
{
Action task = null;
lock (tasks)
{
if (tasks.Count != 0)
{
task = tasks.Dequeue();
}
}
task();
}
}