Preventing UI from freezing while processing data - c#

I am having some problems with BluetoothChat (that I beleive its the same code on bot Java and MonoForAndroid) example app. I have connected my Android to an microcontroller using a Bluetooth module. In case of sending messages (just raw bytes to microcontroller) it works just fine!
The microcontroller streams a constant serial message and I want to read that data. There is a class named MyHandler in BluetoothChat.cs app that has a code block like this:
case MESSAGE_READ:
byte[] readBuf = (byte[])msg.Obj;
// construct a string from the valid bytes in the buffer
var readMessage = new Java.Lang.String (readBuf, 0, msg.Arg1);
bluetoothChat.conversationArrayAdapter.Add(
bluetoothChat.connectedDeviceName + ": " + readMessage);
break;
So what I need to do is to process the incoming raw data and then change color of some buttons, So I made the follwing changes to the code above:
case MESSAGE_READ:
byte[] readBuf = (byte[])msg.Obj;
//I have just added this code and it blocks the UI
bluetoothChat.ProcessIncomingData(readBuff);
break;
And in the BluetootChat activity I have this method:
public void ProcessIncomingData(byte[] readBuf)
{
if (_logBox != null)
{
_logBox.Text += "\r\n"; //TextView
foreach (var b in readBuf)
{
_logBox.Text += (uint)b + " "; //Show the bytes as int value
}
}
}
`
But unfortunately the changes I made stops the UI and the app crashes after f short while.
Any ideas how can I do this neatly without freezing the UI?

You'll want to hand the work off to a background thread in order to keep the UI thread free to respond to input. I wrote up a post awhile back outlining some of the different methods available to you for doing background threads: Using Background Threads in Mono For Android Applications
One thing to be careful with when dealing with background threads is that if you want to make any changes to the UI, you must switch back to the UI thread. You can do this by using the RunOnUiThread() method.

Create a new thread for the process to take place in.
public static void threadProcess()
{
Thread thread = new Thread()
{
public void run()
{
// Process that will run in the thread
}
};
thread.start();
}

Related

Using NetMQ to receive data in a C# application

I'm having trouble making a simple test app that uses NetMQ to receive data from an established network. I'd like to eventually do things with this data, but for now I just need to get basic receiving working. The code is below:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
textOut.Text = "";
var utf8 = new UTF8Encoding();
using (var client = new SubscriberSocket())
{
client.Options.ReceiveHighWatermark = 1000;
client.Connect("tcp://eddn.edcd.io:9500");
client.SubscribeToAnyTopic();
while (true)
{
var bytes = client.ReceiveFrameBytes();
var uncompressed = ZlibStream.UncompressBuffer(bytes);
var result = utf8.GetString(uncompressed);
textOut.Text += result;
//Console.WriteLine(result);
Thread.Sleep(10);
}
}
}
}
When I run this, I see it is doing things in the performance monitor, but the window doesn't come up.
Before I added the NetMW code, the window displayed just fine so I don't think it is a problem with window visibility or anything like that. Any advice on what's going wrong here and how it might be fixed would be greatly appreciated.
Your immediate problem is you have a tight loop.
This:
while (true)
{
var bytes = client.ReceiveFrameBytes();
var uncompressed = ZlibStream.UncompressBuffer(bytes);
var result = utf8.GetString(uncompressed);
textOut.Text += result;
//Console.WriteLine(result);
Thread.Sleep(10);
}
Will run forever.
This is a bad thing.
That loop is running on the UI thread.
This is the thread that does everything.
That includes showing your window.
This problem is compounded by the fact you put that tight loop in your main window constructor.
Don't put substantial code in any UI object constructor. When it fails your ui object will not be constructed.
If that code really needs to loop forever then your socket reading code should be run on a background thread.
That aspect of the question is netmq specific rather than wpf but I would have thought you can open a socket and subscribe a handler which will act as data is received.

How can I coordinate between COM port sends and receives?

I'm trying to refactor some ultra-complex legacy code that sends data from a handheld device to an app running on a PC, to which the handheld device is connected.
There is a "conversation" that goes on between the two apps that follows a protocol; the server (the app running on the PC) responds based on what the client tells it, and vice versa. Actually, the "conversation" can be seen about two thirds of the way down here.
Anyway, my problem is: how can I let the client wait for the server to respond without interrupting it, or thinking it's not going to respond and failing to continue? This is what I have right now:
public class FileXferLegacy : IFileXfer
{
private SerialPort cereal;
private String lastDataReceived;
private String receivedData;
. . .
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// This method will be called when there is data waiting in the port's buffer
try
{
receivedData += cereal.ReadLine();
lastDataReceived = receivedData;
ExceptionLoggingService.Instance.WriteLog(String.Format("Received {0} in FileXferLegacy.SendDataContentsAsXML", receivedData));
}
catch (Exception ex)
{
//MessageBox.Show(ex.Message);
}
}
#region IFileFetchSend Members
. . .
public void SendDataContentsAsXML(string destinationPath, string data)
{
byte[] stuff;
ExceptionLoggingService.Instance.WriteLog("Reached
FileXferLegacy.SendDataContentsAsXML");
cereal.Open();
stuff = System.Text.UTF8Encoding.UTF8.GetBytes("PING" + "\n");
cereal.Write(stuff, 0, stuff.Length);
if (lastDataReceived.Contains("PING")) // Expecting "PING|ACKNOWLEDGE|"
{
stuff =
System.Text.UTF8Encoding.UTF8.GetBytes("LOGIN|foo|000003|LOC_HOST|PPP_PEER|1.4.0.42|bar" + "\n");
// TODO: replace this test data with dynamic data
cereal.Write(stuff, 0, stuff.Length);
}
if (lastDataReceived.Contains("JOIN|LEVEL")) // Expecting something like "JOIN|LEVEL|1
SETTING|ALT_ID|FALSE"
{
stuff = System.Text.UTF8Encoding.UTF8.GetBytes("HHTCOMMAND|GETHHTSUPDATE|");
cereal.Write(stuff, 0, stuff.Length);
}
. . .
String lastResponse = lastDataReceived; // Expecting something like
"RESULT|FILECOMPLETE|INV_000003_whatever(not identical to what was sent earlier!).XML"
// Parse out and do something with the filename ("INV_000003_whatever(not identical to
what was sent earlier!).XML" above)
}
As you can see, the client/handheld sends a string; it then reads "lastDataReceived" which is assigned in the DataReceived method. But what if there has been a delay, and "lastDataReceived" is null? What do I need to do to force a delay (without going to an extreme that would cause the app to appear slothlike in its slowness)? Or what is the way this should be done, if I'm totally off base?
A typical approach is to use a reader thread that pulls bytes off the port with blocking reads (though it can be done with async notification instead) and, once detecting that an entire message has been delivered, it either:
Puts them into a blocking queue (with consumer blocking on calls to dequeue until either a msg is added or a timeout reached
or
Notifies a listener with an event that contains the message.
Which of those two depends a lot on the consumer of those messages. Your code above would benefit from #1, though if the consumer is the UI thread then you should look at #2.
The protocol seems to be half-duplex so rewriting it with synchronous calls to Write/Readline seems to be the simplest way to handle it.

Thread will not close, doesn't seem to reach changed variable

I am working on a multiplayer game, using the lidgren library for networking.
I am currently having issues with a my function that reads messages sent from my server.
The function looks like this:
public class Client
{
/* code omitted */
public void ReadMessage()
{
//Read Messages
while (running)
{
Debug.Log("InREAD");
//wClient is a NetClient (lidgren library)
NetIncomingMessage msg;
while ((msg = wClient.ReadMessage()) != null)
{
switch (msg.MessageType)
{
case NetIncomingMessageType.Data:
if (msg.ReadString().Contains("Position"))
{
Debug.Log("Hej");
/*string temp = msg.ReadString();
string[] Parts = temp.Split(" ");
int x = int.Parse(Parts [1]);
int y = int.Parse(Parts [2]);
int z = int.Parse(Parts [3]);*/
//set player position to xyz values below
} else if (msg.ReadString().Contains("Instantiate"))
{
Debug.Log("Instantiate");
/* string temp = msg.ReadString();
string[] Parts = temp.Split(" ");*/
}
break;
}
}
}
}
}
as you can see, there is a while-loop that runs when the bool running is true (and yes I am setting it as true when declaring.).
Now, in my GUI class where the button for connecting is etc, I have a function call to OnApplicationQuit which looks like this:
void OnApplicationQuit()
{
client.running = false;
client.Disconnect();
Debug.Log(client.running);
Debug.Log("Bye");
}
However, the change of running doesn't reach the thread (I believe the thread is running on a cached version of the variable?). So my question is, how do i make the while-loop stop when the program is closed? (Ive tried calling on the .Abort() function on the thread in the OnApplicationQuit(), but it doesn't work either.
Also, i know its not very efficient to send strings over a network unless you need to (so no need telling me about that!)
Just guessing (since I do not know library lidgren): isn't it possible that you're thread is stuck in call wClient.ReadMessage() just because you are not receiving any message from the client? If wClient.ReadMessage() is a blocking call then the resulting behaviour would be the one you described.
Furthermore: even calling Thread.Abort() won't work because the thread is in a sleep state (since it is waiting for something coming from the network connection): the thread will be aborted as soon as your wClient.ReadMessage() returns. Looking MSDN here it tells that "If Abort is called on a managed thread while it is executing unmanaged code, a ThreadAbortException is not thrown until the thread returns to managed code" and this exactly your situation assuming that ReadMessage() at some point will perform a system call just to wait for some data coming from the underlying socket.
You must call client.Shutdown().

Cannot receive UDP packets inside Unity game

So, I have this game, written in Unity, which is supposed to receive data in real-time over UDP. The data will be coming over wireless from an android device, written in Java, while the Unity program is written in C#. My problem is, whenever I try to declare a UdpClient object, and call its Receive() method inside the Update() method of Unity, the game hangs. Here's the code that I am trying to put inside my Update() method -
UdpClient client = new UdpClient(9877);
IPEndPoint receivePoint = new IPEndPoint(IPAddress.Parse("192.168.1.105"), 9877);
byte[] recData = client.Receive(ref receivePoint);
But it's causing the game to hang.
I then tried a different approach - I tried to receive the data in a separate thread. Works like magic if all I have to do is receive the byte array. No issues. Except that I also need the data received to be used as parameters to functions used in the actual game (for now, let's just say I need to display the received data bytes as a string in the main game window). But, I do not have knowledge of how cross-threading works in Unity. I tried this -
string data = string.Empty;
private IPEndPoint receivePoint;
void OnGUI()
{
GUI.Box(new Rect(20, 20, 100, 40), "");
GUI.Label(new Rect(30, 30, 100, 40), data);
}
void Start()
{
LoadClient();
}
public void LoadClient()
{
client = new UdpClient(9877);
receivePoint = new IPEndPoint(IPAddress.Parse("192.168.1.105"), 9877);
Thread startClient = new Thread(new ThreadStart(StartClient));
startClient.Start();
}
public void StartClient()
{
try
{
while (true)
{
byte[] recData = client.Receive(ref receivePoint);
System.Text.ASCIIEncoding encode = new System.Text.ASCIIEncoding();
data = encode.GetString(recData);
}
}
catch { }
}
But my program hangs if I try the above. So, what exactly am I missing here?
The reason it hangs for you is because that's the way Receive is defined. It blocks your current code until there is data available on the network connection (i.e. the underlying socket). You are correct that you should use a background thread for that.
Please note though, that creating threads in your game object scripts can be dangerous business in case you for example attach the script to multiple objects at the same time. You don't want multiple version of this script running at the same time because they would all try to bind to the same socket address (which won't work).
You also need to pay attention to closing down the threads if the game object dies (this is not automatically done in C# - you have to stop threads).
That said, when you are using multiple threads you need to ensure thread safety. This means that you need to protect the data so that you cannot read it while it is being written to. The simplest way to do this is to use C# locks:
private readonly Object _dataLock = new Object();
private string _sharedData = String.Empty;
void OnGUI()
{
string text = "";
lock (_dataLock)
text = _sharedData;
}
void StartClient()
{
// ... [snip]
var data = Encoding.ASCII.GetString(recData);
lock (_dataLock)
_sharedData = data;
}
Note that locks can hurt performance a bit, especially if used frequently. There are other ways to protect data in c# that are more performant (but slightly more complex). See this guideline from Microsoft for a few examples.

Variable mysteriously changing value

EDIT: Ok I had a problem with one of the string concatenation functions, has nothing to do with threads, but knowing that it couldn't be a problem with threading lead me to the answer thank you for answering.
I am making a simple tcp/ip chat program for practicing threads and tcp/ip. I was using asynchronous methods but had a problem with concurrency so I went to threads and blocking methods (not asynchronous). I have two private variables defined in the class, not static:
string amessage = string.Empty;
int MessageLength;
and a Thread
private Thread BeginRead;
Ok so I call a function called Listen ONCE when the client starts:
public virtual void Listen(int byteLength)
{
var state = new StateObject {Buffer = new byte[byteLength]};
BeginRead = new Thread(ReadThread);
BeginRead.Start(state);
}
and finally the function to receive commands and process them, I'm going to shorten it because it is really long:
private void ReadThread(object objectState)
{
var state = (StateObject)objectState;
int byteLength = state.Buffer.Length;
while (true)
{
var buffer = new byte[byteLength];
int len = MySocket.Receive(buffer);
if (len <= 0) return;
string content = Encoding.ASCII.GetString(buffer, 0, len);
amessage += cleanMessage.Substring(0, MessageLength);
if (OnRead != null)
{
var e = new CommandEventArgs(amessage);
OnRead(this, e);
}
}
}
Now, as I understand it only one thread at a time will enter BeginRead, I call Receive, it blocks until I get data, and then I process it. The problem: the variable amessage will change it's value between statements that do not touch or alter the variable at all, for example at the bottom of the function at: if (OnRead != null) "amessage" will be equal to 'asdf' and at if (OnRead != null) "amessage" will be equal to qwert. As I understand it this is indicative of another thread changing the value/running asynchronously. I only spawn one thread to do the receiving and the Receive function is blocking, how could there be two threads in this function and if there is only one thread how does amessage's value change between statements that don't affect it's value. As a side note sorry for spamming the site with these questions but I'm just getting a hang of this threading story and it's making me want to sip cyanide.
Thanks in advance.
EDIT:
Here is my code that calls the Listen Method in the client:
public void ConnectClient(string ip,int port)
{
client.Connect(ip,port);
client.Listen(5);
}
and in the server:
private void Accept(IAsyncResult result)
{
var client = new AbstractClient(MySocket.EndAccept(result));
var e = new CommandEventArgs(client, null);
Clients.Add(client);
client.Listen(5);
if (OnClientAdded != null)
{
var target = (Control) OnClientAdded.Target;
if (target != null && target.InvokeRequired)
target.Invoke(OnClientAdded, this, e);
else
OnClientAdded(this, e);
}
client.OnRead += OnRead;
MySocket.BeginAccept(new AsyncCallback(Accept), null);
}
All this code is in a class called AbstractClient. The client inherits the Abstract client and when the server accepts a socket it create's it's own local AbstractClient, in this case both modules access the functions above however they are different instances and I couldn't imagine threads from different instances combining especially as no variable is static.
Well, this makes no sense the way you described it. Which probably means that what you think is going on is not what is really happening. Debugging threaded code is quite difficult, very hard to capture the state of the program at the exact moment it misbehaves.
A generic approach is to add logging to your code. Sprinkle your code with Debug.WriteLine() statements that shows the current value of the variable, along with the thread's ManagedId. You get potentially a lot of output, but somewhere you'll see it going wrong. Or you get enough insight in how thread(s) are interacting to guess the source of the problem.
Just adding the logging can in itself solve the problem because it alters the timing of code. Sucks when that happens.
I assume OnRead is firing an event dispatched on a thread pool thread. If any registered event handler is writing to amessage, its value could change any time you're in the reading loop.
Still not very clear where you are gettingthe value assigned to amessage in the loop. Should cleanmessage read content?

Categories