Thread-safe buffer that propagates the latest data - c#

I have a data source which creates (produces) a PointF every 15 to 20 milliseconds.
I need to store (consume) such points every 10ms. My approach is to use a 3 points wide buffer and pointers to achieve a lock-free access:
protected class PosBuffer
{
PointF[] m_Buffer = new PointF[3];
volatile int m_ReadPointer = 0;
volatile int m_WritePointer = 1;
internal PosBuffer()
{
m_Buffer[0] = new PointF(0, 0);
m_Buffer[1] = new PointF(0, 0);
m_Buffer[2] = new PointF(0, 0);
}
internal void Write(PointF point)
{
m_Buffer[m_WritePointer] = point;
m_ReadPointer++;
if (m_ReadPointer == 3) m_ReadPointer = 0;
m_WritePointer++;
if (m_WritePointer == 3) m_WritePointer = 0;
}
internal PointF Read()
{
return m_Buffer[m_ReadPointer];
}
}
My idea is that
as soon as new data arrives it will be stored 'above' the old data. Then the read pointer is set to this position and then the write pointer is incremented.
in case now new data has been produced the consumer thread reads the old data again.
This construction allows different or inconstant read and write rates.
My questions are:
would this approach work?
Do I need locks/monitors/critical sections...
Would I need to disable optimization?
Are there known better solutions?

Try running this code:
async Task Main()
{
var cts = new CancellationTokenSource();
var ct = cts.Token;
var pb = new PosBuffer();
var tw = Task.Run(() =>
{
while (true)
{
if (ct.IsCancellationRequested)
break;
pb.Write(new PointF());
}
});
var tr = Task.Run(() =>
{
while (true)
{
if (ct.IsCancellationRequested)
break;
pb.Read();
}
});
await Task.Delay(TimeSpan.FromSeconds(5.0));
cts.Cancel();
}
Fairly quickly it throws IndexOutOfRangeException. You're letting the value of the "pointers" (bad name by the way) be 3 before dropping back to zero and in the time it takes to change it down the read operation throws.
The problem goes away if you increment like this:
m_ReadPointer = (m_ReadPointer == m_Buffer.Length - 1) ? 0 : m_ReadPointer + 1;
m_WritePointer = (m_WritePointer == m_Buffer.Length - 1) ? 0 : m_WritePointer + 1;
Now, if you have multiple writers then you're definitely going to need locking.

You could consider using a BroadcastBlock<T> from the TPL Dataflow library:
Provides a buffer for storing at most one element at time, overwriting each message with the next as it arrives.
using System.Threading.Tasks.Dataflow;
// Initialize
BroadcastBlock<PointF> block = new(x => x);
// Write the latest value
block.Post(new PointF(0, 0));
// Fetch the latest value
PointF point = await block.ReceiveAsync();
Another idea is to use a BehaviorSubject<T> from the Rx library.
Represents a value that changes over time.
using System.Reactive.Subjects;
// Initialize
BehaviorSubject<PointF> subject = new(new PointF(0, 0));
// Write the latest value
subject.OnNext(new PointF(0, 0));
// Get the latest value
PointF point = subject.Value;
Both classes (BroadcastBlock<T> and BehaviorSubject<T>) are thread-safe.

For your case of single value, I would suggest ReaderWriterLockSlim assuming
you need thread safe reads and writes with multiple threads.
protected class PosBuffer
{
private PointF m_Buffer;
private ReaderWriterLockSlim m_Lock = new();
internal void Write(PointF point)
{
m_Lock.EnterWriteLock();
try
{
m_Buffer = point;
}
finally
{
m_Lock.ExitWriteLock();
}
}
internal PointF Read()
{
m_Lock.EnterReadLock();
try
{
return m_Buffer;
}
finally
{
m_Lock.ExitReadLock();
}
}
}

Related

For loop to make a typing effect not working (c#) [duplicate]

I am working on a WinForm project where I have a label in a for loop. I want to show the label each time after executing the label.text statement. But it doesn't show for every time, rather it shows after for loop is finished.
I tried to achieve this by using Thread.Sleep(). But I can't. Please help me.
NOTE :- lblProgress is a Label
Here's my coding.
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
lblProgress.Text = "Hello World"+i;
Thread.Sleep(10000);
}
Whenever you create a WinForm application, it is spun up into a new process and a new thread is created. Any updates to the User Interface are all done on the same thread as your process. This means when your application is doing "busy work", your UI will be blocked because they are on the same thread. What this means is that, in order to achieve what it is you're trying to achieve, you have to do a little extra work.
First step we need to do is create a function for your work routine (we could use an anonymous function, but since you are new to C#, I think it'll be easier to understand if we break it out), like this:
private void DoWork()
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
lblProgress.Text = "Hello World"+i;
Thread.Sleep(1000); // I changed this from 10000 to 1000 (10 seconds down to 1 second)
}
}
Next, we need to create a new thread that executes our DoWork() function. Its unclear what the "trigger" is for doing your work, but I'm going to assume its a button click:
private void button1_click(object sender, EventArgs e)
{
var work = new Thread(DoWork);
work.Start();
}
So now, whenever someone click the button, we will start a new thread that executes our DoWork function in that thread. The new thread spawns, then execution is immediate returned and our GUI will now update in real time as our thread is executing in the background.
But wait! We still have one more problem to take care of. The problem is that Window's form controls are not thread safe and if we try to update a control from another thread, other then the GUI's thread, we will get a cross-thread operation error. The key to fixing this is to use InvokeRequired and Invoke.
First, we need to make another function that does just the label update:
private void SetProgressLabel(int progress)
{
lblProgress.Text = "Hello World" + progress;
}
In your form class, we also need to create a new delegate:
public partial class Form1 : Form
{
private delegate void ProgressCallback(int progress);
// ..
// The rest of your code
// ..
}
Finally, change your DoWork() method to something like this:
private void DoWork()
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
if (lblProgress.InvokeRequired)
{
lblProgress.Invoke(new ProgressCallback(SetProgressLabel), new object[] { i });
}
else
{
SetProgressLabel(i);
}
Thread.Sleep(1000); // I changed this from 10000 to 1000 (10 seconds down to 1 second)
}
}
This uses the label's (derived from Control) InvokeRequired property to determine if an Invoke is required. It returns true or false. If its false, we can just call our SetProgressLabel() function like we'd normally do. If its true, we must use Invoke to call our function instead.
Congratulations! You just made your first thread safe application.
Now, just as an aside note, you are not properly releasing and disposing of your objects. I recommend you change your DoWork() code to something like this:
private void DoWork()
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout = sourceTable.Rows[i].Field<string>(0);
using (dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString))
{
dest.Open();
using (destcmd = new SqlCommand(checkout, dest))
{
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
if (lblProgress.InvokeRequired)
{
lblProgress.Invoke(new ProgressCallback(SetProgressLabel), new object[] { i });
}
else
{
SetProgressLabel(i);
}
Thread.Sleep(1000); // I changed this from 10000 to 1000 (10 seconds down to 1 second)
}
}
}
}
Because I wrapped your IDisposable's into using blocks, the resources will automatically be disposed of once it goes out of scope.
Although threading would be the more ideal solution another solution is:
Application.DoEvents()
this will give the UI thread time to update.
Example
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
lblProgress.Text = "Hello World"+i;
Application.DoEvents();
}
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout = sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
var task = Task.Factory.StartNew(() =>
{
//Thread.Sleep(1000);
lblProgress.Text = "Hello World" + i;
}, CancellationToken.None, TaskCreationOptions.None, ui);
task.Wait();
}
});
If you are executing the mentioned code on the UI thread, UI will be refreshed only after entire for loop is executed. Based on your needs, progress bar/background worker kind of set up looks suitable.

Asynchronous Task, video buffering

I am trying to understand Tasks in C# but still having some problems. I am trying to create an application containing video. The main purpose is to read the video from a file (I am using Emgu.CV) and send it via TCP/IP for process in a board and then back in a stream (real-time) way. Firstly, I did it in serial. So, reading a Bitmap, sending-receiving from board, and plotting. But reading the bitmaps and plotting them takes too much time. I would like to have a Transmit, Receive FIFO Buffers that save the video frames, and a different task that does the job of sending receiving each frame. So I would like to do it in parallel. I thought I should create 3 Tasks:
tasks.Add(Task.Run(() => Video_load(video_path)));
tasks.Add(Task.Run(() => Video_Send_Recv(video_path)));
tasks.Add(Task.Run(() => VideoDisp_hw(32)));
Which I would like to run "parallel". What type of object should I use? A concurrent queue? BufferBlock? or just a list?
Thanks for the advices! I would like to ask something. I am trying to create a simple console program with 2 TPL blocks. 1 Block would be Transform block (taking a message i.e. "start" ) and loading data to a List and another block would be ActionBlock (just reading the data from the list and printing them). Here is the code below:
namespace TPL_Dataflow
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Random randn = new Random();
var loadData = new TransformBlock<string, List<int>>(async sample_string =>
{
List<int> input_data = new List<int>();
int cnt = 0;
if (sample_string == "start")
{
Console.WriteLine("Inside loadData");
while (cnt < 16)
{
input_data.Add(randn.Next(1, 255));
await Task.Delay(1500);
Console.WriteLine("Cnt");
cnt++;
}
}
else
{
Console.WriteLine("Not started yet");
}
return input_data;
});
var PrintData = new ActionBlock<List<int>>(async input_data =>
{
while(input_data.Count > 0)
{
Console.WriteLine("output Data = " + input_data.First());
await Task.Delay(1000);
input_data.RemoveAt(0);
}
});
var linkOptions = new DataflowLinkOptions { PropagateCompletion = true };
loadData.LinkTo(PrintData, input_data => input_data.Count() >0 );
//loadData.LinkTo(PrintData, linkOptions);
loadData.SendAsync("start");
loadData.Complete();
PrintData.Completion.Wait();
}
}
}
But it seems to work in serial way.. What am I doing wrong? I tried to do the while loops async. I would like to do the 2 things in parallel. When data available from the List then plotted.
You could use a TransformManyBlock<string, int> as the producer block, and an ActionBlock<int> as the consumer block. The TransformManyBlock would be instantiated with the constructor that accepts a Func<string, IEnumerable<int>> delegate, and passed an iterator method (the Produce method in the example below) that yields values one by one:
Random random = new Random();
var producer = new TransformManyBlock<string, int>(Produce);
IEnumerable<int> Produce(string message)
{
if (message == "start")
{
int cnt = 0;
while (cnt < 16)
{
int value;
lock (random) value = random.Next(1, 255);
Console.WriteLine($"Producing #{value}");
yield return value;
Thread.Sleep(1500);
cnt++;
}
}
else
{
yield break;
}
}
var consumer = new ActionBlock<int>(async value =>
{
Console.WriteLine($"Received: {value}");
await Task.Delay(1000);
});
producer.LinkTo(consumer, new() { PropagateCompletion = true });
producer.Post("start");
producer.Complete();
consumer.Completion.Wait();
Unfortunately the producer has to block the worker thread during the idle period between yielding each value (Thread.Sleep(1500);), because the TransformManyBlock currently does not have a constructor that accepts a Func<string, IAsyncEnumerable<int>>. This will be probably fixed in the next release of the TPL Dataflow library. You could track this GitHub issue, to be informed about when this feature will be released.
Alternative solution: Instead of linking explicitly the producer and the consumer, you could keep them unlinked, and send manually the values produced by the producer to the consumer. In this case both blocks would be ActionBlocks:
Random random = new Random();
var consumer = new ActionBlock<int>(async value =>
{
Console.WriteLine($"Received: {value}");
await Task.Delay(1000);
});
var producer = new ActionBlock<string>(async message =>
{
if (message == "start")
{
int cnt = 0;
while (cnt < 16)
{
int value;
lock (random) value = random.Next(1, 255);
Console.WriteLine($"Producing #{value}");
var accepted = await consumer.SendAsync(value);
if (!accepted) break; // The consumer has failed
await Task.Delay(1500);
cnt++;
}
}
});
PropagateCompletion(producer, consumer);
producer.Post("start");
producer.Complete();
consumer.Completion.Wait();
async void PropagateCompletion(IDataflowBlock source, IDataflowBlock target)
{
try { await source.Completion.ConfigureAwait(false); } catch { }
var ex = source.Completion.IsFaulted ? source.Completion.Exception : null;
if (ex != null) target.Fault(ex); else target.Complete();
}
The main difficulty with this approach is how to propagate the completion of the producer to the consumer, so that eventually both blocks are completed. Obviously you can't use the new DataflowLinkOptions { PropagateCompletion = true } configuration, since the blocks are not linked explicitly. You also can't Complete manually the consumer, because in this case it would stop prematurely accepting values from the producer. The solution to this problem is the PropagateCompletion method shown in the above example.

LockRecursionException in multiple threaded function

I dont know how to describe it but I'm getting exception that schouldn't have a place when a code is good written. This exception is about issue with ReaderWriterLockSlim and it's LockRecursionException; it is appearing at "ScreenLocker.EnterReadLock();" line. Can't find problem with my code and description what to do or what might be wrong at internet, that's why i writting this question here and asking you all for help. This is code I have problem with:
public static List<Dictionary<int, int>> RunTasks(ScreenScanning ss)
{
var listOfTasks = new List<Task>();
List<Dictionary<int, int>> PosXOfBlocksAndMeaningOfIt = new List<Dictionary<int, int>>();
for (var i = 0; i <= BlocksOnYAxisOnScreen; i++)
{
ScreenLocker.EnterReadLock();
var t = new Task(() =>
{
PosXOfBlocksAndMeaningOfIt.Add(ss.XAxysScan(PosYOfRowsToScan[i], Screen, ref ScreenLocker));
});
listOfTasks.Add(t);
}
Task.WaitAll(listOfTasks.ToArray());
return PosXOfBlocksAndMeaningOfIt;
}
and that are functions called by this method:
public Dictionary<int, int> XAxysScan(int posY, Bitmap screen, ref ReaderWriterLockSlim screenLocker)
{
screenLocker.ExitReadLock();
Dictionary<int, int> partOfMainTable = new Dictionary<int, int>();
partOfMainTable.Add(666, posY); //used in BotViewUpdate in DataToTableInterpreter
for (int i = 0; i <= 1920; i++)
{
if (screen.GetPixel(i, posY) == ColorsInRow[0])
{
if (IsFarmable(posY, ColorsInRow, i, screen))
{
partOfMainTable.Add(i, 1);
}
}
else if (IsBackground(BackgroundColors, i, posY, screen))
{
partOfMainTable.Add(i, 0);
}
else
{
partOfMainTable.Add(i, 2);
}
}
return partOfMainTable;
}
How can you see I'm releaseing lock right after entering XAxysScan function.
How can you see I'm releasing lock right after entering XAxysScan function.
The ReaderWriterLockSlim is a synchronization object that allows multiple threads to read from a resource, but only allow 1 resource to write to it(ideally).
The reason why this is important is because the specific way that ReaderWriterLockSlim is implemented to achieve this effect, requires something called Managed Thread Affinity, which basically means that whatever Task or Thread that called the EnterReadLock() must be the same Task or thread that calls ExitReadLock();.
When we look at the following, we can see you Have RunTasks(ScreenScanning ss) enter the lock, but you immediately start a new child Task and pass the ReaderWriterLockSlim as a reference to XAxysScan().
ScreenLocker.EnterReadLock();
var t = new Task(() =>
{
PosXOfBlocksAndMeaningOfIt.Add(ss.XAxysScan(PosYOfRowsToScan[i], Screen, ref ScreenLocker));
});
Only the same Task that enters a lock can be the one to release that lock. At least for synchronization objects like ReaderWriterLockSlim that use Managed Thread Affinity.
Consider moving EnterReadLock() into the XAxysScan() method.
public Dictionary<int, int> XAxysScan(int posY, Bitmap screen, ref ReaderWriterLockSlim screenLocker)
{
screenLocker.EnterReadLock();
try{
Dictionary<int, int> partOfMainTable = new Dictionary<int, int>();
partOfMainTable.Add(666, posY); //used in BotViewUpdate in DataToTableInterpreter
for (int i = 0; i <= 1920; i++)
{
if (screen.GetPixel(i, posY) == ColorsInRow[0])
{
if (IsFarmable(posY, ColorsInRow, i, screen))
{
partOfMainTable.Add(i, 1);
}
}
else if (IsBackground(BackgroundColors, i, posY, screen))
{
partOfMainTable.Add(i, 0);
}
else
{
partOfMainTable.Add(i, 2);
}
}
return partOfMainTable;
}
finally
{
// make sure that even if we encounter an error, we still exit the lock so other threads can enter the lock / begin writing
screenLocker.ExitReadLock();
}

How can I de-reference an object in C#?

I have a buffer that cycles between two indices and I want to write out the object at the current index in a task and allow the rest of the program to continue processing things. I have attempted to simplify the process while maintaining all the pertinent parts.
object[] buffer = new object[2]
int currentIndex = 0
while(true){
buffer[currentIndex].field1 = newdatahere //data grabbed by sensor bundle
buffer[currentIndex].field2 = newdatahere //data grabbed by camera bundle
buffer[currentIndex].field3 = newdatahere //data grabbed from system snapshot
task.factory.starnew(()=>{
writeOutObject(buffer[currentIndex])
}
buffer[currentIndex] = new object();
currentIndex = 1 - currentIndex //cycle between the 0 and 1 indices
}
void writeOutObject(Object obj){
//do file IO here
//write out field1, field2, field3
}
The problem is that by assigning the buffer item to a new object I am killing the writeOutObject method because the obj no longer exists by the time the task runs. I want to be able to keep the old object until it is written out and have the buffer point to a new object.
What I want to do:
object obj1 = new object();
obj1.field1 = data1;
obj1.field2 = data2;
obj1.field3 = data3;
obj2 = obj1;
//de-reference obj1 from the object that it was pointed to and associate it to a new object
// i want this to write out data1,data2,data3 but instead it is
// writing out data4,data5,data6 or some mixture because it has
// been overwritten halfway through the file IO
task.factory.startnew(()=>{ write out obj2 }
obj1.field1 = data4;
obj1.field2 = data5;
obj1.field3 = data6;
Maybe something like:
obj1 = new object()
obj2* = &obj1
obj1* = &new object
I need to break the reference of obj1 back to obj2 once it has been assigned. Simply doing this won't work:
obj1 = new object()
obj2 = obj1
obj1 = null // or new object()
As requested, "The Real Code"
CancellationTokenSource cts = new CancellationTokenSource();
public void StartMachine()
{
Task.Factory.StartNew(() =>
{
_isFirstData = true;
_expiredFlag = false;
Plc.StartPLC();
Plc.Start();
while (true)
{
if (!_paused && !Plc.IsInputStackEmpty() && !Plc.IsOutputSlideOpen())
{
CameraFront.SnapAquire();
// If this is the first data set the wait handles
if (!_isFirstData)
{
CameraBack.SnapAquire();
}
else
{
_imageBackRecieved.Set();
_databaseInfoRecieved.Set();
//_isFirstCard = false;
}
// Wait for 3 things! Image Front, Image Back, Database
bool gotEvents = WaitHandle.WaitAll(_waitHandles, TIMEOUT);
if (gotEvents)
{
if (!_isFirstData)
{
if (Buffer[1 - NextDataOutIndex].IsDataComplete())
{
if (Buffer[1 - NextDataOutIndex].EvaluateData())
{
OnPassFailNotification()
Plc.Pass();
}
else
{
OnPassFailNotification()
Plc.Fail();
}
}
else
{
OnPassFailNotification()
Plc.Fail();
Common.Logging
}
}
else
{
_isFirstData = false;
}
}
else
{
Common.Logging("WARNING: Wait handle timed out"
Plc.Fail();
}
Data temp = Buffer[1 - NextDataOutIndex];
Task.Factory.Startnew(()=>{
Data.WriteData(temp);
}
Buffer[1 - NextDataOutIndex] = new Data();
// Swap card buffers - alternate between 1 and 0
NextdataOutIndex = 1 - NextDataOutIndex;
// Do this
Plc.WheelAdvance();
}
else
{
}
}
}, cts.Token);
}
public static void WriteData(Data data)
{
if(WRITE_BATCH_FILES)
try
{
if (data.ImageFront != null)
{
string filenameforfront = "blahlbah-front.tiff";
OperatorSet.WriteImage(data.ImageFront, "tiff", 0, filenameforfront);
}
if (data.ImageBack != null)
{
string filenameforback = "blahblah-back.tiff";
HOperatorSet.WriteImage(data.ImageBack, "tiff", 0, filenameforback);
}
}
catch (Exception ex)
{
Common.Logging.
//throw ex;
}
//TODO: Write out data in xml
//TODO: Write out metrics
}
just before you task.factory.StartNew do the following
while(...)
{
... bunch of other code
buildTask(buffer[currentIndex]);
buffer[currentIndex] = new object();
... bunch of other code
}
// Within this method, all references to detachedBuffer object will remain pointing to the same
// memory location no matter whether the variable passed in is reassigned.
public void buildTask(object detachedBuffer)
{
task.factory.starnew(()=>{
writeOutObject(detachedBuffer);
};
}
Sounds like a job for Semaphores!
Semaphores are a form of inter-thread communication that are ideal for this situation as they allow one thread to lock the semaphore but another to release it again. In the code sample below, the sem.WaitOne() line will wait until the sem.Release() method has been called. This blocks your main thread for just long enough that your task gets hold of the data it needs.
object[] buffer = new object[2]
int currentIndex = 0
while(true){
buffer(currentIndex).field1 = newdatahere //data grabbed by sensor bundle
buffer(currentIndex).field2 = newdatahere //data grabbed by camera bundle
buffer(currentIndex).field3 = newdatahere //data grabbed from system snapshot
Semaphore sem = new Semaphore(1,1); //Initialise the semaphore so that it is checked out
task.factory.starnew(()=>{
object item = buffer[currentIndex]; //Create local reference to the data item
sem.Release(); //Check-in the semaphore (let the WaitOne method return)
writeOutObject(item)
}
sem.WaitOne(); //Block until the semaphore has returned
buffer[currentIndex] = new object();
currentIndex = 1 - currentIndex //cycle between the 0 and 1 indices
}
void writeOutObject(Object obj){
//do file IO here
//write out field1, field2, field3
}

Getting an exception while trying to receive objects over TCP with c#

I am trying to receive objects with TCP using C# and serialization. I am receiving objects constantly and each object is sent to a new task. I chose not to use threads because its too expensive. The problem is that if I am receiving only 1 object at a time everything goes just fine but if I am trying to receive more than 1 object, after a few seconds I am getting:
"the input stream is not a valid binary format. the starting contents (in bytes) are: ..."
This is my listening function:
public void Listen()
{
try
{
TcpObject tcpObject = new TcpObject();
IFormatter formatter = new BinaryFormatter();
bool offline = true;
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(() => offline = Offline));
while (!offline)
{
tcpObject = (TcpObject)formatter.Deserialize(serverStream);
if (tcpObject.Command == Command.Transfer)
{
#region Task
Task.Factory.StartNew(() =>
{
SentAntenna sentAntenna = (SentAntenna)tcpObject.Object;
string antennaName = sentAntenna.Name;
if (MainWindow.SpectrumList.ContainsKey(antennaName))
{
PointCollection pointCollection = new PointCollection();
float minChan = sentAntenna.Min;
float maxChan = sentAntenna.Max;
if (MainWindow.SpectrumList[antennaName].spectrumViewModel.AbsoluteMinimum == -1)
{
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(delegate
{
MainWindow.SpectrumList[antennaName].spectrumViewModel.AbsoluteMinimum = minChan;
MainWindow.SpectrumList[antennaName].spectrumViewModel.AbsoluteMaximum = maxChan;
MainWindow.SpectrumList[antennaName].spectrumViewModel.TBMinRange = minChan.ToString();
MainWindow.SpectrumList[antennaName].spectrumViewModel.TBMaxRange = maxChan.ToString();
MainWindow.SpectrumList[antennaName].spectrumViewModel.MinRange = minChan;
MainWindow.SpectrumList[antennaName].spectrumViewModel.MaxRange = maxChan;
MainWindow.SpectrumList[antennaName].spectrumViewModel.UpdateRange();
}));
}
float gap = maxChan - minChan;
foreach (Frequency f in sentAntenna.Frequencies)
{
float chan = ((f.Channel - minChan) / gap) * 310;
float inten = ((f.Intensity - 1) / 599) * 100;
pointCollection.Add(new Point(chan, inten));
}
pointCollection.Freeze();
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(() => MainWindow.SpectrumList[antennaName].spectrumViewModel.AllAntennaPoints = pointCollection.Clone()));
}
Thread.Sleep(50);
});
#endregion
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message); // raise an event
}
}
What am I doing wrong?
Try moving
SentAntenna sentAntenna = (SentAntenna)tcpObject.Object;
to the line before the StartNew(). I believe this will fix your issue.
I don't think you want concurrent access to the tcpObject, since it's global to all the tasks.
Alternatively you could instantiate the TcpObject inside the while loop, which would then keep it local to each task.
There are several reasons for this error:
When two objects concurently writes to one connection
When somthing goes wrong with serverStream: stream have received a part of data or received 0 length data
You concurently acces to tcpObject. it's a bad idea.

Categories