Background: My application receives asynchronous sensor data from a variety of IoT devices, displays it in several windows, and manages a physical system based on the data. Data are not archived. I am currently managing the data a C# dataset which contains several different datatables, each with just one row. The datatable structures never change (add, remove rows, etc.) but individual values change constantly. I am using a dataset/datatables architecture because its change notification plumbing is transparent; value updates are propagated throughout the application automatically and GUI databinding is easy.
Problem: Asynchronous updates to datatables are not thread-safe and eventually the dataset gets corrupted because multiple threads are making changes simultaneously.
Question: How can I consume asynchronous, dynamic data in a thread-safe manner and propagate changed values throughout my application... without complex plumbing?
CodePudding user response:
In general, application development will be easier if only selected parts is run on background threads, with all other parts running on the UI thread.
I assume you need multiple threads for reading from the sensor data. So make sure all the threading stuff is managed inside the reader for the sensor. And when you get new data, do whatever you do to inform the rest of the application of the data on the UI thread.
If you want some more flexibility you could write an adapter that does this, or inject an optional synchronization context.
CodePudding user response:
I understand your requirement as: you have many DataTables
in the DataSet
, each table contains exactly one row and this is never aded or removed while several threads access them. But each row's fields are modified. If that's true it should be sufficient to lock
the table:
public static void ModifyField<TValue>(this DataSet dataSet, string tableName, string columnName, TValue value)
{
if (!dataSet.Tables.Contains(tableName))
{
throw new ArgumentException($"Table <{tableName}> not contained in DataSet", nameof(tableName));
}
DataTable table = dataSet.Tables[tableName] ?? throw new InvalidOperationException($"Table <{tableName}> is null");
DataColumn column = table.Columns[columnName] ?? throw new InvalidOperationException($"Table <{tableName}> does not contain column <{columnName}>");
DataRow row = table.AsEnumerable().FirstOrDefault() ?? throw new InvalidOperationException($"Table <{tableName}> is empty");
lock (table)
{
row.SetField<TValue>(column, value);
}
}