before posting the question i did my research for 10 days so really hope someone can shed some light into solving this issue.
The issue is that any bindable control, does not update once the binding list from singleton class is changed. This is a common issue on multi-threaded apps. Most if not all solutions offer suggestions where the bindlinglist or collection is initialized from parent thread, and then some invocation to be made. Not what i'm looking for. The same issue persist if static class is used instead of singleton.
Basically, the application triggers some Tasks, which in turn create object(s) on different business classes. These objects post messages into the bindinglist, which should update the UI listbox, but does not. And yes, the message object is in the list, and binding after the TASK finished works (items displayed). Locking/unlocking object(s) access is also not an issue.
Appreciate any suggestions/solutions
A trimmed down version of business objects:
namespace MyNameSpace
{
public class Message
{
private string messageSummary;
public Message() { }
public string MessageSummary
{
set { messageSummary = value; }
get { return messageSummary; }
}
}
}
A trimmed down version of another class doing some ops:
namespace MyNameSpace
{
public class WorkDoingClass
{
public WorkDoingClass() { }
public void DoSomeWork()
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSOmrWork Finished";
}
public void DoSomeOtherWork()
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSomeOtherWork Finished";
AllMessages.Instance.AllMessagesBindingList.Add(messageObj);
}
}
}
Singleton:
namespace MyNameSpace
{
public sealed class AllMessages
{
private static readonly AllMessages _instance = new AllMessages();
private BindingList<Message> _allMessagesBL;
public WorkDoingClass() { _allMessagesBL = new BindingList<Message>(); }
public static AllMessages Instance
{
get { return _instance; }
}
public BindingList<Message> AllMessagesBindingList
{
get { return _allMessagesBL};
}
}
}
This is also a trimmed down version from where calls start:
namespace MyNameSpace
{
public partial class Form1 : Form
{
private Task _TaskSqlData;
private CancellationTokenSource cTokenSourceSql;
public Form1()
{
InitializeComponent();
listBox1.DataSource = AllMessages.Instance.AllMessagesBindingList;
listBox1.DisplayMember = "MessageSummary";
}
private void button1_Click(object sender, EventArgs e)
{
cTokenSourceSql = new CancellationTokenSource();
var tokenSqlData = cTokenSourceSql.Token;
if (this._TaskSqlData != null)
{
if (this._TaskSqlData.Status == TaskStatus.Running)
this.cTokenSourceSql.Cancel();
this._TaskSqlData.Dispose();
this._TaskSqlData = null;
}
_TaskSqlData = Task.Factory.StartNew(()
=> StartDoingWork(this, tokenSqlData, null), tokenSqlData);
}
public void StartDoingWork(object sender, CancellationToken ct, EventArgs e)
{
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
WorkDoingClass work = new WorkDoingClass();
work.DoSomeOtherWork();
}
Your problem is that the thread(the main UI thread) making the listbox is different from the thread(the worker thread) modifying the collection.
Try the following code. It could solve your issue. I use SynchronizationContext to synchronize the two threads, which serves as the same function with Control.Invoke().
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Task _TaskSqlData;
private CancellationTokenSource cTokenSourceSql;
WorkDoingClass _work;
public Form1()
{
InitializeComponent();
listBox1.DataSource = AllMessages.Instance.AllMessagesBindingList;
listBox1.DisplayMember = "MessageSummary";
_work = new WorkDoingClass(SynchronizationContext.Current);
}
private void button1_Click(object sender, EventArgs e)
{
cTokenSourceSql = new CancellationTokenSource();
var tokenSqlData = cTokenSourceSql.Token;
if (this._TaskSqlData != null)
{
if (this._TaskSqlData.Status == TaskStatus.Running)
this.cTokenSourceSql.Cancel();
this._TaskSqlData.Dispose();
this._TaskSqlData = null;
}
_TaskSqlData = Task.Factory.StartNew(()
=> StartDoingWork(this, tokenSqlData, null), tokenSqlData);
}
public void StartDoingWork(object sender, CancellationToken ct, EventArgs e)
{
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
_work.DoSomeOtherWork();
}
}
public class Message
{
private string messageSummary;
public Message() { }
public string MessageSummary
{
set { messageSummary = value; }
get { return messageSummary; }
}
}
public class WorkDoingClass
{
private SynchronizationContext _syncContext;
public WorkDoingClass() { }
public WorkDoingClass(SynchronizationContext _syncContext)
{
// TODO: Complete member initialization
this._syncContext = _syncContext;
}
public void DoSomeWork()
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSOmrWork Finished";
}
public void DoSomeOtherWork()
{
_syncContext.Send(DoWork, null);
}
private static void DoWork(object arg)
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSomeOtherWork Finished";
AllMessages.Instance.AllMessagesBindingList.Add(messageObj);
}
}
public sealed class AllMessages
{
private static readonly AllMessages _instance = new AllMessages();
private BindingList<Message> _allMessagesBL;
public AllMessages() { _allMessagesBL = new BindingList<Message>(); }
public static AllMessages Instance
{
get { return _instance; }
}
public BindingList<Message> AllMessagesBindingList
{
get { return _allMessagesBL; }
}
}
}
Related
I implemented an observer pattern using events and delegates. The program is receiving and processing big amounts of data (around 3000 messages per second) but at some point, it starts sending messages with a delayed timestamp, which I am trying to fix. I have 3 main classes that do the job in my opinion:
public class MessageTracker : IObservable<MessageEventArgs>
{
private List<IObserver<MessageEventArgs>> observers;
public MessageTracker()
{
observers = new List<IObserver<MessageEventArgs>>();
}
private static readonly MessageTracker mInstance = new MessageTracker();
private static MessageTracker getInstance() => mInstance;
private class Unsubscriber : IDisposable
{
private List<IObserver<MessageEventArgs>> _observers;
private IObserver<MessageEventArgs> _observer;
public Unsubscriber(List<IObserver<MessageEventArgs>> observers, IObserver<MessageEventArgs> observer)
{
this._observers = observers;
this._observer = observer;
}
public void Dispose()
{
if (! (_observer == null)) _observers.Remove(_observer);
}
}
public IDisposable Subscribe(IObserver<MessageEventArgs> observer)
{
if (! observers.Contains(observer))
observers.Add(observer);
return new Unsubscriber(observers, observer);
}
public void MessageTrack(MessageEventArgs msg) {
observers.AsParallel().ForAll(observer =>
{
if (msg is null)
observer.OnError(new ArgumentException("MessageError."));
else
observer.OnNext(msg);
});
}
public void EndMessageTrans(){
foreach(var observer in observers.ToArray())
if (observers.Contains(observer))
observer.OnCompleted();
observers.Clear();
}
}
public class MessageReporter : IObserver<MessageEventArgs>
{
private IDisposable unsubscriber;
public MessageReporter()
{ }
public event EventHandler<MessageEventArgs> OnNextMessage;
public virtual void Subscribe(IObservable<MessageEventArgs> provider)
{
if (provider != null)
unsubscriber = provider.Subscribe(this);
}
public void OnCompleted()
{
this.Unsubscribe();
}
public void OnError(Exception error)
{
}
public void OnNext(MessageEventArgs value)
{
if (OnNextMessage != null)
{
OnNextMessage?.Invoke(this, value);
}
}
public virtual void Unsubscribe()
{
unsubscriber.Dispose();
}
}
public sealed class MessageDataWorker
{
private readonly bool mSubscribeAll;
private readonly IEnumerable<string> mMessages;
public MessageDataWorker(IEnumerable<string> messages)
{
mMessages = messages;
if ((mMessages?.Count() ?? 0) == 0)
mSubscribeAll = true;
}
public override void DoWork()
{
var messageReporter = new MessageReporter();
messageReporter.OnNextMessage += OnNewMessageReceived;
messageReporter.Subscribe(MessageTracker.GetInstance());
while (!mShouldStop.WaitOne(100)) ;
MessageReporter.Unsubscribe();
}
private void OnNewMessageReceived(object sender, MessageEventArgs e)
{
if (!mSubscribeAll && !mMessages.Contains(e.Message))
return;
string message = "Message|" +
$"{e.Time}|" +
$"{e.Text};
try
{
Console.WriteLine(message);
}
catch { }
}
}
What I am trying to achieve is skipping notifications or receiving data for X milliseconds after sending the last message and afterward send the newest received message. I tried sleeping the observers and the provider but it just increased the delay. I think I am missing something and any suggestion would be appreciated.
From what I can tell from your code you could write the three classes with this code:
var messageTrack = new Subject<MessageEventArgs>();
var query =
from e in messageTrack
where !mMessages.Contains(e.Message)
select $"Message|{e.Time}|{e.Text}";
query.Throttle(TimeSpan.FromMilliseconds(X)).Subscribe(Console.WriteLine);
You should never need to implement IObservable<> or IObserver<> yourself. It almost always ends in disaster.
The above code handles the throttling you wanted.
I have a M2MQTT client subscribed to a topic in the DashboardViewModel class. On message receive, the UI gets updated by calling Writelog.
public class DashboardViewModel : Object, IDashboardViewModel
{
private IDashboardView View { get; }
public DashboardViewModel(IDashboardView view)
{
View = view;
mqttClient = new MqttClient("localhost");
mqttClientId = Guid.NewGuid().ToString();
mqttClient.MqttMsgPublishReceived += mqttClient_MsgPublishReceived;
mqttClient.Subscribe(new string[] { "Home/Temperature" }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
mqttClient.Connect(mqttClientId);
//...
}
private void mqttClient_MsgPublishReceived(object sender, MqttMsgPublishEventArgs eventArgs)
{
string message = Encoding.UTF8.GetString(eventArgs.Message);
View.Writelog(message);
}
}
The textbox on FrmMain does not update; tbxLogs.InvokeRequired always returns false, i.e. tbxLogs.AppendText always executes. Any suggestions please?
public partial class FrmMain : Form, IDashboardView
{
private IDashboardViewModel dashboardViewModel = null;
private delegate void WriteLogCallback(string text);
public FrmMain()
{
InitializeComponent();
}
public void Writelog(string text)
{
if (tbxLogs.InvokeRequired)
{
WriteLogCallback callback = new WriteLogCallback(Writelog);
tbxLogs.Invoke(callback, new object[] { text });
}
else
{
tbxLogs.AppendText(text + "\n");
}
}
}
I think you need to use dispatcher :)
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action<String>(Writelog), message);
inside of this method
private void mqttClient_MsgPublishReceived(object sender, MqttMsgPublishEventArgs eventArgs)
{
string message = Encoding.UTF8.GetString(eventArgs.Message);
//here instead of View.Writelog(message);
}
The example below functions fine, but I want to have the Complete event fire its event handlers in the UI thread. I don't want HasCompleted() to have to worry about checking if it's on the UI thread or not. Calls to HasCompleted() should always be invoked on the UI thread. How do I do this?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var game = new Game();
game.Complete += HasCompleted;
game.Load();
}
private void HasCompleted()
{
if (label1.InvokeRequired)
{
label1.BeginInvoke(new Action(() => label1.Text = "complete"));
}
else
{
label1.Text = "complete";
}
}
}
public class Game
{
public Game()
{
}
public event MethodInvoker Complete;
public void Load()
{
var task = new Task(new Action(() =>
{
Thread.Sleep(500);
OnComplete();
}));
task.Start();
}
private void OnComplete()
{
if (Complete != null)
{
Complete();
}
}
}
Capture the current synchronization context when you create the Game object and use that to marshal the event to the context that was current when the object was first created:
public class Game
{
private SynchronizationContext context;
public Game()
{
context = SynchronizationContext.Current ??
new SynchronizationContext();
}
public MethodInvoker Complete;
public void Load()
{
//...
}
private void OnComplete()
{
if (Complete != null)
{
context.Post(_ => Complete(), null);
}
}
}
This is my main form class and inside i have Stop button click event:
public partial class MainWin : Form
{
private Job job = new...
private void btnStop_Click(object sender, EventArgs e)
{
job.state = true;
}
}
When my stop button clicked i change my job class member from false to true and what i want to do is when this variable changed to true i want to access to specific method inside job class and do something.
public class Job
{
public bool state { get; set; }
private void processFile() // i want access to this method in order to change other class state
{
// do work
}
}
how can i do it ?
It's really hard to tell what you exactly mean, but one way to invoke a method when the property is set would be to expand the auto property out and do exactly that.
public class Job
{
private bool state;
public bool State
{
get { return this.state; }
set
{
this.state = value;
processFile();
}
private void processFile()
{
// do work
}
}
However, just guessing and seeing this little bit of code, you might want to redesign how you're doing things.
If really don't want to expose you private method, you can do something like this:
public class Job
{
private bool state;
public bool State
{
get
{
return state;
}
set
{
if (state != value)
{
state = value;
OnStateChanged();
}
}
}
private void OnStateChanged()
{
if (state) // or you could use enum for state
Run();
else
Stop();
}
private void Run()
{
// run
}
private void Stop()
{
// stop
}
}
But you should really consider creating public Job.Run method and leaving Job.State readonly. If you want the object to perform some operations, the methods will be more suitable for this.
Create the Job class like this:
public class Job
{
private bool _isRunning = false;
public bool IsRunning { get { return _isRunning; } }
public void StartProcessing()
{
if (_isRunning)
{
// TODO: warn?
return;
}
ProcessFile();
}
public void StopProcessing()
{
if (!_isRunning)
{
// TODO: warn?
return;
}
// TODO: stop processing
}
private void ProcessFile()
{
_isRunning = true;
// do your thing
_isRunning = false;
}
}
Then consume it like this:
public partial class MainWin : For
{
private Job _job = new Job();
private void StartButton_Click(object sender, EventArgs e)
{
if(!_job.IsRunning)
{
_job.StartProcessing();
}
}
private void StopButton_Click(object sender, EventArgs e)
{
if(_job.IsRunning)
{
_job.StopProcessing();
}
}
}
Thread safety left out as exercise.
I'm unsure as to what is the best approach for passing events down the line to parent classes and in need of some feedback.
The example code below tries to illustrate what I want to achieve.
namespace test {
public delegate void TestCompletedEventHandler(object sender,
TestCompletedEventArgs e);
public class Manager {
CarList m_carlist = null;
public CarList Cars {
get { return m_carlist; }
set { m_carlist = value; }
}
public Manager() {
Cars = new CarList(this);
}
public void Report(bool successfull) {
//...
}
}
public class CarList : List<Car> {
protected internal event TestCompletedEventHandler
Car_TestCompleted = null;
protected readonly Manager m_manager = null;
public Manager Manager {
get { return m_manager; }
}
public CarList(Manager manager) {
m_manager = manager;
}
public void Test() {
foreach(Car car in this) {
bool ret = car.Test();
manager.Report(ret);
}
}
public void Add(Car car) {
//Is this a good approach?
car.TestCompleted +=
new TestCompletedEventHandler(Car_TestCompleted_Method);
base.Add(car);
}
private void Car_TestCompleted_Method(object sender,
TestCompletedEventArgs e)
{
if(Car_TestCompleted != null) Car_TestCompleted(sender, e);
}
}
public class Car {
protected internal event TestCompletedEventHandler
TestCompleted = null;
public bool Test() {
//...
if(TestCompleted != null) TestCompleted(this,
new TestCompletedEventArgs())
}
}
public class TestCompletedEventArgs : EventArgs {
//...
}
}
using test;
Manager manager = new Manager();
manager.Cars.Car_TestCompleted +=
new TestCompletedEventHandler (Car_TestCompleted_Method);
manager.Cars.Test();
Another more specific example:
//Contains DataItems and interfaces for working with them
class DataList
{
public List<DataItem> m_dataitems { get; set; }
public TestManager m_testmanager { get; set; }
// ...
}
class DataItem
{
// ...
}
//A manager class for running tests on a DataList
class TestManager
{
public List<TestSource> m_sources { get; set; }
public WorkerManager m_workermanager { get; set; }
// ...
}
//A common interface for Tests
abstract class TestSource
{
public event EventHandler<EventArgs<object>> Completed = null;
protected TestManager m_owner { get; set; }
public abstract void RunAsync();
// ...
}
//A test
class Test1 : TestSource
{
public virtual void RunAsync()
{
//Add commands
//Run workers
//Report progress to DataList and other listeners (like UI)
//Events seem like a bad approach since they need to be forwarded through many levels of abstraction
if(Completed != null) Completed(this, new EventArgs<object>(null));
}
// ...
}
//Manages a number of workers and a queue of commands
class WorkerManager
{
public List<MyWorker> m_workers { get; set; }
public Queue<Command> m_commands { get; set; }
}
//Wrapper for BackgroundWorker
class MyWorker
{
// ...
}
//Async command
interface Command
{
// ...
}
I think you may have just over implemented this a bit... It looks like you are trying to use async operations. Even if you are using sync operations though, typically you'd just use callback methods instead of events in a case like this...
Here is an example of things to change to use callbacks here:
//new delegate
public delegate void CarReportCallback(Car theCar, bool result);
//in the Manager class, make report conform to delegate's signature
public void Report(Car theCar, bool result)
{
//do something, you know which car and what the result is.
}
//in the CarList class pass a reference to the report method in
public void Test()
{
foreach(Car car in this)
{
car.Test(manager.Report);
}
}
//in the Car class use the delegate passed to invoke the reporting
public void Test(CarReportCallback callback)
{
//... do stuff
callback(this, isTestCompleted);
}
It seems reasonable, but I'm not really sure what the use case is and how this would be used.
You've got a strong concept of containment going on, but I'm not really sure why. Also, it's kind of weird that the CarList 'sort of' seems to have ownership of the individual cars.
Additionally, I don't know why Test() on the Car class would both return a result and raise an event. It seems like you're having two different paths to return the same data. And the Manager class seems completely redundant with the CarList class at first glance.
What is the problem you're actually trying to solve here? That might help me with defining a good solution to it.
It wouldn't make sense to just have each car call an event which calls an event on the parent list. I would do it more like this:
namespace test {
public delegate void TestCompletedEventHandler(object sender,
TestCompletedEventArgs e);
public class Manager {
CarList m_carlist = null;
public CarList Cars {
get { return m_carlist; }
set { m_carlist = value; }
}
public Manager() {
Cars = new CarList(this);
}
public void Report(bool successful) {
//...
}
}
public class CarList : List<Car> {
protected readonly Manager m_manager = null;
protected List<Action<object, TestCompletedEventArgs>> delegatesList = new List<Action<object, TestCompletedEventArgs>>();
public Manager Manager {
get { return m_manager; }
}
public CarList(Manager manager) {
m_manager = manager;
}
public void Test() {
foreach(Car car in this) {
bool ret = car.Test();
manager.Report(ret);
}
}
public void Add(TestCompletedEventHandler e) {
foreach (Car car in this) {
car.OnTestCompleted += e;
}
delegatesList.Add(e);
}
public void Add(Car car) {
foreach(Action a in delegatesList)
{
car.OnTestCompleted += a;
}
base.Add(car);
}
}
public class Car {
protected internal event TestCompletedEventHandler OnTestCompleted = null;
public bool Test() {
//...
if (OnTestCompleted != null) OnTestCompleted(this, new TestCompletedEventArgs());
}
}
public class TestCompletedEventArgs : EventArgs {
//...
}
}
using test;
Manager manager = new Manager();
Manager.Cars.Add(new Car());
manager.Cars.Add(new Car());
manager.Cars.Add(new Car());
manager.Cars.Add((sender, args) =>
{
//do whatever...
})
manager.Cars.Test();
manager.Cars.Add(new Car());