Asynchronously add value to Bindinglist/Cross-threading & Locking issue - c#

I have a BindingList databound to a datgridview. I'm using it to keep track of some real-time prices. The method 'update(Quote quote)' is called multiple times a second by various threads. If the datagridview doesn't contain the Quote, it is added. If it does, the values of the quote are updated. I don't want the same quote to appear in the BindingList (or on the GUI) twice, so I tried to put a lock around the operation that checks whether the value is in the list or not. It doesn't work! What am I doing wrong? I've tried two different ways of locking, and am locking on a String object rather than just an object. The problem is definitely in the BeginInvoke(new MethodInvoker(delegate() { activeQuotes.Insert(0, quote); })); call (which is probably taking some time), but if I make that synchronous, the 'add' method throws a 'cross-threading' error. . . What can I do to avoid the cross-threading error, but ensure that the lock works also??
public BindingList<Quote> activeQuotes = new BindingList<Quote>();
object lockObject = "lockObject";
dataGridViewActive.DataSource = activeQuotes;
public void update(Quote quote)
{
//lock (lockObject)
if(Monitor.TryEnter(lockObject))
{
try
{
if (!activeQuotes.Contains(quote))
{
try
{
activeQuotes.Add(quote);
AddQuote(quote);
}
catch (Exception ex)
{
Console.WriteLine("Datagridview!!!!!!");
}
}
else
{
int index = activeQuotes.IndexOf(quote);
activeQuotes[index].Bid = quote.Bid;
activeQuotes[index].Ask = quote.Ask;
activeQuotes[index].Mid = quote.Mid;
activeQuotes[index].Spread = quote.Spread;
activeQuotes[index].Timestamp = quote.Timestamp;
}
finally
{
Monitor.Exit(lockObject);
}
}
private void AddQuote(Quote quote)
{
if (this.InvokeRequired)
{
BeginInvoke(new MethodInvoker(delegate() { activeQuotes.Insert(0, quote); }));
BeginInvoke(new MethodInvoker(delegate() { dataGridViewActive.Refresh(); }));
BeginInvoke(new MethodInvoker(delegate() { dataGridViewActive.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells); }));
}
else
{
activeQuotes.Add(quote);
dataGridViewActive.Refresh();
dataGridViewActive.AutoResizeColumns (DataGridViewAutoSizeColumnsMode.AllCells);
}
}
I'd appreciate any help at all on this.
Thanks.

this code I wrote before will do
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
BindingListInvoked<Name> names;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
names = new BindingListInvoked<Name>(dataGridView1);
dataGridView1.DataSource = names;
new Thread(() => names.Add(new Name() { FirstName = "Larry", LastName = "Lan" })).Start();
new Thread(() => names.Add(new Name() { FirstName = "Jessie", LastName = "Feng" })).Start();
}
}
public class BindingListInvoked<T> : BindingList<T>
{
public BindingListInvoked() { }
private ISynchronizeInvoke _invoke;
public BindingListInvoked(ISynchronizeInvoke invoke) { _invoke = invoke; }
public BindingListInvoked(IList<T> items) { this.DataSource = items; }
delegate void ListChangedDelegate(ListChangedEventArgs e);
protected override void OnListChanged(ListChangedEventArgs e)
{
if ((_invoke != null) && (_invoke.InvokeRequired))
{
IAsyncResult ar = _invoke.BeginInvoke(new ListChangedDelegate(base.OnListChanged), new object[] { e });
}
else
{
base.OnListChanged(e);
}
}
public IList<T> DataSource
{
get
{
return this;
}
set
{
if (value != null)
{
this.ClearItems();
RaiseListChangedEvents = false;
foreach (T item in value)
{
this.Add(item);
}
RaiseListChangedEvents = true;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
}
}
}
public class Name
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}

I think you should change your BeginInvoke to just Invoke. You need to get it on the UI thread, not begin an async operation. Otherwise your lock could get released before the BeginInvoke target gets invoked because control is returned immediately upon calling BeginInvoke. Calling Invoke will block that thread on that call until the Invoke target completes then return control back to your thread, which will ensure the lock is kept.
Also, have you considered using a lock block instead of Monitor method calls? It's basically the same thing but prevents you from needing the try/finally. I don't see that you're using any retry or benefit from the TryEnter, but perhaps the code sample doesn't demonstrate that.

Related

Singleton Async Loading List Property

I have a winforms application with a singleton class that contains a property (list) that is the datasource for a search grid. Filling this property takes a significant amount of time (>1 minute) so, I want to start filling this property asynchronously when the user launches the program.
The main form has a button to launch another search form. If, when the user launches the search if the datasource is ready then, no problem. However, if the datasource is still filling, the user sees a wait cursor and the search grid should fill as soon as the datasource has finished populating.
To do this, I created a method that fires after the asynchronous method completes and then the grid is bound to the datasource.
Everything appears to work correctly, the event fires, and then I try to bind the list to the grid and nothing happens... Debugging halts and I never hit the next line of code (see comments in FrmSearch.cs).
Any ideas relating to what is going wrong or general code improvements would be very much appeciated, thanks!
Program.cs
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Task.Run(async() => { Singleton.DogsList = await Dogs.FindAllAsync(); });
Application.Run(new FrmMain());
}
}
Singleton.cs
public static class Singleton
{
public static event DogsListHandler DogsListLoaded;
public delegate void DogsListHandler(object sender, EventArgs e);
public static BindingList<Dogs> DogsList
{
get
{
if (dogsList == null)
{
Task.Run(async () =>
{
dogsList = await Dogs.FindAllAsync();
notifyListLoaded();
});
}
return dogsList;
}
set { dogsList = value; }
}
private static BindingList<Dogs> dogsList;
private static void notifyListLoaded()
{
if (DogsListLoaded != null) { DogsListLoaded(null, EventArgs.Empty); }
}
}
FrmSearch.cs
public partial class FrmSearch : Form //launched using the .Show() method from a button on the main form
{
public FrmSearch()
{
InitializeComponent();
}
private void FrmSearch_Load(object sender, EventArgs e)
{
Singleton.DogsListLoaded += new Singleton.DogsListHandler(Dogs_ListLoaded);
Cursor = Cursors.WaitCursor;
if (Singleton.DogsList != null)
{
grid.DataSource = Singleton.DogsList;
Cursor = Cursors.Default;
}
else { Cursor = Cursors.WaitCursor; }
}
public void Dogs_ListLoaded(object sender, EventArgs e)
{
grid.DataSource = Singleton.DogsList; //freezes here
Cursor = Cursors.Default; //this line never gets hit
}
}
Dogs.cs (will pull from db normally but just doing some iteration for the sample)
public class Dogs
{
public string Name { get; set; }
public string Breed { get; set; }
public int Age { get; set; }
public string Summary { get { return string.Format("Name: {0} / Breed: {1} / Age: {2}", Name, Breed, Age.ToString()); } }
public static async Task<BindingList<Dogs>> FindAllAsync()
{
BindingList<Dogs> dl = new BindingList<Dogs>();
await Task.Run(() =>
{
int i = 0;
while (i <= 999999)
{
dl.Add(new Dogs() { Name = "River" + i.ToString(), Breed = "Border Collie", Age = 3 });
dl.Add(new Dogs() { Name = "Jack" + i.ToString(), Breed = "Labrador", Age = 2 });
dl.Add(new Dogs() { Name = "Emma" + i.ToString(), Breed = "Beagle", Age = 7 });
i++;
}
});
return dl;
}
}
You should retrieve the list as you would retrieve all other lists getting from anywhere:
Call a async method and await that.
Easy to handle
public static class SomeLookups
{
private static object _lock = new object( );
private static Task<IList<string>> _foosAsync;
public static Task<IList<string>> GetFoosAsync()
{
lock ( _lock )
{
return _foosAsync = _foosAsync ?? PrivateGetFoosAsync( );
}
}
private static async Task<IList<string>> PrivateGetFoosAsync()
{
var list = new List<string>( );
for ( int i = 0; i < 20; i++ )
{
await Task.Delay( 200 ).ConfigureAwait( false );
list.Add( "item " + i );
}
return list.AsReadOnly( );
}
}
In the consuming class
private async Task RetrieveAllData()
{
IsBusy = true;
MyFooCollection = await SomeLookups.GetFoosAsync();
IsBusy = false;
}
Once the task is finished you can still await it. But because it is finished you get the result without any delay.

WPF Memory not released when called from ActiveX

I have a class that populates 1000 ComboBoxes into the UI, the code is
namespace ActiveXMemory
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
PopulateControls();
}
public void PopulateControls() {
var newGrid = new Grid();
for (var i = 0; i < 1000; i++)
{
ComboBox newcombo = new ComboBox();
newGrid.Children.Add(newcombo);
}
this.Content = newGrid;
}
public bool OpenWindow(bool isRunning)
{
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
try
{
this.ShowDialog();
}
catch (Exception exp)
{
Console.WriteLine(exp.Message);
}
}));
return true;
}
public bool CloseWindow()
{
Dispatcher.Invoke(DispatcherPriority.Render, new Action(() =>
{
try
{
this.Close();
}
catch (Exception exp)
{
//Console.WriteLine(exp.Message);
}
}));
return true;
}
private void Window_Closed(object sender, EventArgs e)
{
var grid = Content as Grid;
var children = grid.Children;
while (children.Count > 0)
{
var child = children[0];
grid.Children.Remove(child);
child = null;
}
grid = null;
}
}
}
I created an ActiveX library for the class to be accessible as ActiveX,
namespace ActiveXLibrary
{
[ComVisible(true)]
[Guid("EF2EAD91-68A8-420D-B5C9-E30A6F510BDE")]
public interface IActiveXLib
{
[DispId(1)]
bool Initialize();
[DispId(2)]
bool CloseActiveX();
}
[ComVisible(true)]
[Guid("9DACD44F-0237-4F44-BCB9-0E6B729915D6"),
ClassInterface(ClassInterfaceType.None)]
[ProgId("Samp")]
public class ActiveXLib : IActiveXLib
{
MainWindow form;
Thread thr;
public bool Initialize()
{
int i = 0;
try
{
ThreadStart exeFunc = new ThreadStart(() =>
{
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Send, new Action(() =>
{
start();
}));
});
thr = new Thread(exeFunc);
thr.SetApartmentState(ApartmentState.STA);
thr.Start();
while (form == null)
{
i++;
//Console.WriteLine("form Null");
System.Threading.Thread.Sleep(1000);
if (i > 30)
break;
}
return true;
}
catch (Exception exp)
{
Console.WriteLine("[Initialize]" + exp.Message);
return false;
}
}
public bool CloseActiveX()
{
bool success = false;
try
{
success = form.CloseWindow();
form = null;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
return success;
}
catch (Exception exp)
{
Console.WriteLine("[CloseActiveX]" + exp.Message);
return false;
}
}
private void start()
{
try
{
Console.WriteLine("start() - new MainWindow()");
form = new MainWindow();
Console.WriteLine("OpenWindow()");
form.OpenWindow(true);
}
catch (Exception exp)
{
MessageBox.Show(exp.Message + "\nPossible Reasons: " + exp.Source + "\n" + exp.InnerException, "Error");
}
}
}
}
Then I created a demo project as
ActiveXLib activeXRef;
public MainWindow()
{
InitializeComponent();
}
private void startButton_Click(object sender, RoutedEventArgs e)
{
activeXRef = new ActiveXLib();
activeXRef.Initialize();
}
private void stopButton_Click(object sender, RoutedEventArgs e)
{
activeXRef.CloseActiveX();
GC.Collect();
}
Here the problem is every time I click the start and stop button, the memory keeps on increasing, the memory (17MB) created by the first start of the application did not get released, even though I removed the ComboBoxes and set the grid content null. Even the GC.Collect() has no effect, since it only adds the garbage to a queue. Is there any fix for this.
I also tried flushing the memory as in the link, But even then the memory is still held.(since I am not sure whether the handle of the current process is from demo project/from ActiveXMemory)
Edit
I included the line
this.Dispatcher.Invoke(DispatcherPriority.Render, GCDelegate);
to the Window_Closed event
Now the memory gets cleared
But one problem I am facing now is, if I try to immediately close (call CloseActiveX()), then this is not happening.(i.e)
private void startButton_Click(object sender, RoutedEventArgs e)
{
activeXRef = new ActiveXLib();
activeXRef.Initialize();
activeXRef.CloseActiveX();
activeXRef = null;
GC.Collect();
}
The above code still has the memory locked, I don't understand the difference, it is just another event,Does anyone have any idea on this?
I think I got a repro for this problem although I can't tell what your MainWindow.Open/CloseWindow() methods look like. Using a thread is certainly part of the problem, there is one non-intuitive thing you have to do to prevent leaking internal WPF plumbing objects that have thread affinity.
It is imperative to shutdown the dispatcher for the thread. This normally just happens once in a WPF app when the UI thread terminates. Also very important that the dispatcher does the dispatching, WPF heavily depends on it to ensure that "weak events" are actually weak.
A sample implementation for MainWindow that does not leak:
public class MainWindow : Window {
public void OpenWindow(bool noidea) {
this.ShowDialog();
}
public bool CloseWindow() {
this.Dispatcher.Invoke(() => {
this.Dispatcher.InvokeShutdown(); // <== Important!
});
return true;
}
}
Where ShowDialog() ensures the dispatcher does the dispatching and the InvokeShutdown() ensures that the cleanup occurs. Tested by using a 10 msec DispatcherTimer instead of buttons so this happened at a very high rate. I could remove the GC.Collect() calls and memory usage was stable.
At the moment I've no comment about the ActiveX part (will double check and let you know), but I can suggest something related to the WPF code.
This should make the combo items disposable
public class DispCombo : ComboBox, IDisposable
{
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
public class DispGrid : Grid, IDisposable
{
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
You will populate disposable controls
public void PopulateControls()
{
var newGrid = new DispGrid();
for (var i = 0; i < 1000; i++)
{
DispCombo newcombo = new DispCombo();
newGrid.Children.Add(newcombo);
}
this.Content = newGrid;
}
So the closing phase would become (disposing instead of setting to null)
private void Window_Closed(object sender, EventArgs e)
{
var grid = Content as DispGrid;
var children = grid.Children;
while (children.Count > 0)
{
var child = children[0] as DispCombo;
grid.Children.Remove(child);
child.Dispose();
}
grid.Dispose();
}
That said, now you can wrap the WPF view in a using clause
public class MainLibrary
{
public bool OpenWindow() //found no ref to bool isRunning
{
using (var mw = new MainWindow())
{
mw.ShowDialog();
mw.Close();
}
return true;
}
public bool CloseWindow()
{
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
return true;
}
}
with your garbage collection in the close.
These are my memory snapshots after these changes, taken (1) at start, then (2) after the main ShowDialog in the OpenWindow and finally (3) after the CloseWindow
Edit
Finally, if you need to start a Task, calling the Dispatcher, here it is the function
public bool CloseWindow()
{
winClose = () =>
{
mw.Dispatcher.Invoke(() =>
{
mw.Close();
mw.Dispose();
});
};
cleanProc = () =>
{
mw.Dispatcher.Invoke(() =>
{
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
});
};
Task.Run(() =>
{
winClose.Invoke();
}).ContinueWith(x =>
{
cleanProc.Invoke();
});
return true;
}
with
public bool OpenWindow()
{
mw = new MainWindow();
mw.Show();
return true;
}

Freezing GUI Elements

im new here and always find helpful threads here, but not in this case. I'm having an issue with my program using threads. In special, my thread is doing some image pattern in the backround and everything seems to be fine.
It also works an undefined time (sometimes 15 s, sometimes minutes) without any exception or freeze or whatever. But then my GUI freezes, not my whole GUI, just the GUI parts updated from the thread. Two other pictureboxes are working fine(streaming a video), but the rest isnt working.
Trying to stop the thread where it is, works, but starting it from there makes my program collapse.
There arn't eny exceptions thrown. Every GUI element is updated via Invoke() if necessary. I only work on a copy of the Picture to avoid any lockmodes or anything else. Also i try to let the UI doing what it needs to do (DoEvents())
Some ideas?
Code:
namespace My.Name.Space
{
public class MyThread : MyThreadBase
{
public MyThread ( getting object s from the form for updating UI elements)
{
//referencing objects
Stopwatch.Start();
}
//example Method for UI updating
private void UpdateRobot1Box(int angle, int x, int y)
{
if (_rob1.InvokeRequired)
{
_rob1.Invoke(new Action(() => _rob1.Clear()));
_rob1.Invoke(new Action(() => _rob1.Text = angle.ToString() + "°, X:" + x.ToString() + ", Y:" + y.ToString()));
}
else
{
_rob1.Clear();
_rob1.Text = angle.ToString() + "°, X:" + x.ToString() + ", Y:" + y.ToString();
}
}
protected override void Loop(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
if( PictureBox != null && Stopwatch.ElapsedMilliseconds >= tick)
{
//DoWork
Application.DoEvents();
}
else
{
Thread.Sleep(1);
}
}
}
}
}
}
Edit 1:
MyThreadBase:
namespace My.Name.Space
{
public abstract class MyThreadBase : DisposableBase//just some simple gc stuff
{
private CancellationTokenSource _cancellationTokenSource;
public bool IsAlive
{
get { return _cancellationTokenSource != null; }
}
public event Action<Object, Exception> UnhandledException;
public void Start()
{
if (_cancellationTokenSource != null)
return;
lock (this)
{
if (_cancellationTokenSource != null)
return;
_cancellationTokenSource = new CancellationTokenSource();
var thread = new Thread(RunLoop) {Name = GetType().Name};
thread.Start();
}
}
public void Stop()
{
if (_cancellationTokenSource == null)
return;
lock (this)
{
if (_cancellationTokenSource == null)
return;
_cancellationTokenSource.Cancel();
}
}
public void Join()
{
while (IsAlive) Thread.Sleep(1);
}
private void RunLoop()
{
try
{
CancellationToken token = _cancellationTokenSource.Token;
Loop(token);
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception exception)
{
OnException(exception);
}
finally
{
lock (this)
{
CancellationTokenSource cancellationTokenSource = _cancellationTokenSource;
_cancellationTokenSource = null;
cancellationTokenSource.Dispose();
}
}
}
protected abstract void Loop(CancellationToken token);
protected virtual void OnException(Exception exception)
{
Trace.TraceError("{0} - Exception: {1}", GetType(), exception.Message);
Trace.TraceError(exception.StackTrace);
OnUnhandledException(exception);
}
protected virtual void OnUnhandledException(Exception exception)
{
if (UnhandledException != null)
UnhandledException(this, exception);
}
protected override void DisposeOverride()
{
Stop();
}
}
The UpdateRobot1Box is called in a switch-case construct within the thread. I got a little for-squence where I go through my list of own created objects to decide what to write in my textbox.
Create a method in main form class to perform the UI update actions:
private void AsyncFormUpdate(Action action)
{
if (this.InvokeRequired)
{
this.Invoke(action, null);
}
else
{
action();
}
}
Having that in proper place you are sure that InvokeRequired acts properly, well, and the code is better encapsulated.
Next be simple. Use the asynchronously invoked delegate with feedback to report angle and coordinates changes to UI, where you'll actually call the AsyncFormUpdate method.
A good example is shown here:
http://www.csharp-examples.net/asynchronous-method-progress/
There they update the progress, where as you'll update the angle and X/Y coordinates.

How to implement monitor and system.threading for the smokers situation

The problem that I am working on deals with out to use a functional Lock, or monitor structure, to provided exclusive access to only one member on separate threads. Below, is my class definition of the monitor (note that it is different than the actual monitor class given by c# library). What I am trying to do is make pictureboxes appear or disappear on my form.
I have attempted to add in an instance of the form so I can access the individual pictureboxes, however, my program seems to freeze.
namespace SmokersProblem
{
class monitor
{
Form1 form = new Form1();
Random num = new Random();
object obj = new object();
public monitor()
{
}
public void agent(){
form.pictureBox4.Visible = false;
int r = num.Next(1, 4);
if (r == 1)
{
// put lighter and paper on table
smoker1();
}
else if (r == 2)
{
// put lighter and tobacco on table
smoker2();
}
else
{
// put paper and tobacco on table
smoker3();
}
}
public void smoker1()
{
//lock (obj)
//{
form.pictureBox9.Visible = true;
form.pictureBox1.Visible = false;
System.Threading.Thread.Sleep(5000);
//agent();
// }
}
public void smoker2()
{
//lock (obj)
//{
form.pictureBox10.Visible = true;
form.pictureBox3.Visible = false;
System.Threading.Thread.Sleep(5000);
//agent();
//}
}
public void smoker3()
{
//lock (obj)
//{
form.pictureBox11.Visible = true;
form.pictureBox2.Visible = false;
System.Threading.Thread.Sleep(5000);
//agent();
// }
}
}
}
Below is my form code, as you can see here, i try to create three seperate threads, one for each smoker.
namespace SmokersProblem
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Random rnd = new Random();
int num = rnd.Next(1, 4);
Object obj = new Object();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
pictureBox1.Visible = true;
pictureBox2.Visible = true;
pictureBox3.Visible = true;
pictureBox8.Visible = false;
pictureBox7.Visible = false;
pictureBox6.Visible = false;
monitor one = new monitor();
one.agent();
Thread vone = new Thread(one.smoker1);
Thread two = new Thread(one.smoker2);
Thread three = new Thread(one.smoker3);
vone.Start();
two.Start();
three.Start();
}
}
}
After implementing this, I went looking for the Smoker Thread Problem that it looks like OP is trying to implement. This code should be easily adaptable to that problem.
The reason your UI is freezing is that you're calling one.agent() without putting it in a new thread. one.agent() sleeps, which keeps your UI from processing events.
OK, I've implemented some code to do the smoker problem with labels. Obviously it could be improved, for instance by not coupling the form to the threads.
I put two different locking mechanisms in, and left one commented out.
Essentially, there are three labels that can either be "Smoking" or "Not Smoking". The main UI thread creates three threads:
Smoker1
Smoker2
Smoker3
Each of the threads continually tries to take the lock in a while loop. When they take the lock, they set their label to "Smoking", wait a few seconds, and then set their label to "Not Smoking". This uses thread safe code from this answer.
public partial class Form1 : Form
{
private bool running = false;
public Label OneLabel { get; set; }
public Label TwoLabel { get; set; }
public Label ThreeLabel { get; set; }
private MyMonitor one;
private Thread vone;
private Thread two;
private Thread three;
public Form1()
{
InitializeComponent();
OneLabel = new Label();
OneLabel.Text = "Not Smoking";
OneLabel.Location = new Point(10, 50);
OneLabel.AutoSize = true;
this.Controls.Add(OneLabel);
TwoLabel = new Label();
TwoLabel.Text = "Not Smoking";
TwoLabel.Location = new Point(150, 50);
this.Controls.Add(TwoLabel);
ThreeLabel = new Label();
ThreeLabel.Text = "Not Smoking";
ThreeLabel.Location = new Point(300, 50);
this.Controls.Add(ThreeLabel);
}
private void MainButton_Click(object sender, EventArgs e)
{
if (!running)
{
vone.Start();
two.Start();
three.Start();
MainButton.Text = "Stop";
running = true;
}
else
{
one.RequestStop();
MainButton.Text = "Run";
running = false;
}
}
private void Form1_Load(object sender, EventArgs e)
{
one = new MyMonitor(this);
vone = new Thread(one.Smoker1);
two = new Thread(one.Smoker2);
three = new Thread(one.Smoker3);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (running)
{
one.RequestStop();
running = false;
}
}
}
class MyMonitor
{
private int x = 1;
private Object obj = new Object();
private Form1 _form;
bool _finished = false;
public MyMonitor(Form1 form)
{
_form = form;
}
public void Smoker1()
{
while (!_finished)
{
//lock (obj)
//{
// _form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Smoking");
// System.Threading.Thread.Sleep(2000);
// _form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Not Smoking");
//}
try
{
Monitor.Enter(obj);
try
{
_form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Smoking");
System.Threading.Thread.Sleep(2000);
_form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Not Smoking");
}
finally
{
Monitor.Exit(obj);
}
}
catch (SynchronizationLockException SyncEx)
{
Console.WriteLine(SyncEx.Message);
}
}
}
public void Smoker2()
{
while (!_finished)
{
//lock (obj)
//{
// _form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Smoking");
// System.Threading.Thread.Sleep(2000);
// _form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Not Smoking");
//}
try
{
Monitor.Enter(obj);
try
{
_form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Smoking");
System.Threading.Thread.Sleep(2000);
_form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Not Smoking");
}
finally
{
Monitor.Exit(obj);
}
}
catch (SynchronizationLockException SyncEx)
{
Console.WriteLine(SyncEx.Message);
}
}
}
public void Smoker3()
{
while (!_finished)
{
//lock (obj)
//{
// _form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Smoking");
// System.Threading.Thread.Sleep(2000);
// _form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Not Smoking");
//}
try
{
Monitor.Enter(obj);
try
{
_form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Smoking");
System.Threading.Thread.Sleep(2000);
_form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Not Smoking");
}
finally
{
Monitor.Exit(obj);
}
}
catch (SynchronizationLockException SyncEx)
{
Console.WriteLine(SyncEx.Message);
}
}
}
public void RequestStop()
{
_finished = true;
}
}
//Thread Safe Extension Method
public static class Extensions
{
private delegate void SetPropertyThreadSafeDelegate<TResult>(Control #this, Expression<Func<TResult>> property, TResult value);
public static void SetPropertyThreadSafe<TResult>(this Control #this, Expression<Func<TResult>> property, TResult value)
{
var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;
if (propertyInfo == null ||
!#this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
#this.GetType().GetProperty(propertyInfo.Name, propertyInfo.PropertyType) == null)
{
throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
}
if (#this.InvokeRequired)
{
#this.Invoke(new SetPropertyThreadSafeDelegate<TResult>(SetPropertyThreadSafe), new object[] { #this, property, value });
}
else
{
#this.GetType().InvokeMember(propertyInfo.Name, BindingFlags.SetProperty, null, #this, new object[] { value });
}
}
}
my program seems to freeze
My one.agent() is the part of the code that allows one of the smokers
to be called on, so they can smoke. Why wouldnt I want it in the main
code?
Because you shouldn't be using Sleep() from the main UI thread, which is what happens when you call one.agent() from the Button Click event. When Sleep(5000) is hit, you're telling the forms main UI thread to not do ANYTHING for five seconds, thus the freezing you're seeing.
To fix this, you'd need agent() to execute smoker1(), smoker2(), or smoker3() in a separate thread like you're doing down below.
There are several other problems with the code, however, that must also be addressed before you can "fix" your code...
The next problem lies in you creating a new instance of Form1() inside your monitor() class. This instance of Form1() is not the same one that is visible on your screen. Acting upon it is modifying an invisible form that has never even been shown. To act upon the form that is actually visible on your screen would require you to either (a) pass a reference to it into your monitor() class when you create it, or (b) have your monitor() class raise custom events that Form1() subscribes to, again when it creates monitor().
The last problem lies in you attempting to change UI controls from within a thread other than the main UI thread. This will result in a cross-thread exception (unless you've turn this off, which you shouldn't). There are various ways to overcome this problem, the most basic of which involves using delegates and the Invoke() method of the Form/Control to which you are trying to update.

marshalling events to ui thread using Synchronisation Context

All the examples I have seen using SynchronisationContext.Post have been used in the same class. What I have is the UI thread passing some by-ref arguments to a threadwrapper class, updating the arguments and then I want it to update some labels etc on the UIThread.
internal class ConnThreadWrapper
{
....
public event EventHandler<MyEventArgs<String, Boolean>> updateConnStatus =
delegate { };
public void updateUIThread(string conn, bool connected)
{
uiContext.Post(new SendOrPostCallback((o) =>
{
updateConnStatus(this,
new MyEventArgs<String, Boolean>(conn,
connected));
}),
null);
}
}
//on ui thread
public void updateConnStatus(object sender, MyEventArgs<String, Boolean> e)
{
switch (e.val1)
{
case "CADS" :
if (e.val2 == true)
{
}
The Event seems to fire without any errors but nothing is ever received on the uiThread - i'm not sure if my signature for the sub updateConnStatus is correct or if it works like this. I obviously want the event to handled on the uithread and update the labels from that sub.
In a previous vb.net project I used to reference the form directly on the thread and used a delegate to invoke a callback but apparently this was a bad design as I was mixing application layers. I wanted to use the sync context as it was meant to be thread safe but most of the examples i've seen have used invoke.
Any ideas what I'm missing? Thanks
I wrote this helper class which works for me. Prior to using this class call InitializeUiContext() on UI thread somewhere on application start.
public static class UiScheduler
{
private static TaskScheduler _scheduler;
private static readonly ConcurrentQueue<Action> OldActions =
new ConcurrentQueue<Action>();
public static void InitializeUiContext()
{
_scheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
private static void ExecuteOld()
{
if(_scheduler != null)
{
while(OldActions.Count > 0)
{
Action a;
if(OldActions.TryDequeue(out a))
{
UiExecute(_scheduler, a);
}
}
}
}
private static void UiExecute(TaskScheduler scheduler,
Action a,
bool wait = false)
{
//1 is usually UI thread, dunno how to check this better:
if (Thread.CurrentThread.ManagedThreadId == 1)
{
a();
}
else
{
Task t = Task.Factory.StartNew(a,
CancellationToken.None,
TaskCreationOptions.LongRunning,
scheduler);
if (wait) t.Wait();
}
}
public static void UiExecute(Action a, bool wait = false)
{
if (a != null)
{
if (_scheduler != null)
{
ExecuteOld();
UiExecute(_scheduler, a, wait);
}
else
{
OldActions.Enqueue(a);
}
}
}
}
In the end I ditched the ThreadWrapper and trying to marshal the event to the UI Thread and used a Task instead, in fact I think I can use task to do most of the stuff in this project so happy days.
Task<bool> t1 = new Task<bool>(() => testBB(ref _bbws_wrapper));
t1.Start();
Task cwt1 = t1.ContinueWith(task => { if (t1.Result == true) { this.ssi_bb_conn.BackColor = Color.Green;} else { this.ssi_bb_conn.BackColor = Color.Red; } }, TaskScheduler.FromCurrentSynchronizationContext());
.....
private static bool testBB(ref BBWebserviceWrapper _bbwsw)
{
try
{
//test the connections
if (_bbwsw.initialize_v1() == true)
{
if (_bbwsw.loginUser("XXXXXXXX", "XXXXXXXXX") == true)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
catch
{
return false;
}
}

Categories