I'm working on a C# Winforms project which loads images in a grid, I implemented parallelism and thread to learn a bit about it and there's something I can't figure out. I need to change the BorderStyle of a set of pictureboxes in run time, here's my code:
Task.Factory.StartNew(() =>
{
Parallel.For(0, img.Count, i =>
{
Bitmap tmp_b = new Bitmap((System.Drawing.Image)img[i].RenderImage(0));
imagenes[i] = tmp_b;
Progress_Bar_Loading_Images.Invoke((Action)delegate { reportarprogreso(); });
});
for (int i = inicio_set; i < final_set; ++i)
{
picbox[i].Image = imagenes[i];
picbox[i].BorderStyle = BorderStyle.FixedSingle;
}
});
The problem is in this line: picbox[i].BorderStyle = BorderStyle.FixedSingle; I get an error message saying: Control '' accessed from a thread other than the thread it was created on.
I think I know why this is happening but I'm not quite sure how to solve it. I tried putting the for (int i = inicio_set; i < final_set; ++i) outside the Task but the images won't get assigned to the pictureboxes since it's a different thread (main) and the images are not available until the task completes so I think I need to update the BorderStyle the same way I update the ProgressBar but not sure how that would be.
Thank you,
Matias.
You execute the for loop in a different thread.
Change the for body to:
if (picbox[i].InvokeRequired)
{
picbox[i].Invoke(new Action(() =>
{
picbox[i].BorderStyle = BorderStyle.FixedSingle;
}));
}
else
{
picbox[i].BorderStyle = BorderStyle.FixedSingle;
}
or simply use Async - Await on the task; make the caller method Async and the task:
await Task.Factory.StartNew(...)
Related
This question already has answers here:
How to put delay before doing an operation in WPF
(2 answers)
Closed last month.
I have a simple C# .NET WPF app that should display all pictures of a folder, each for half a second using an image element. PollPic is a property (of variable pollPic). currImageFilename was declared above. I aimed to use Invoke/BeginInvoke in order to update the UI. The function where this code belongs to is a async function, that is called (with await) from a button click event.
When I have 6 pictured in the folder, each has been read and and sleep was called 6 times but only the last picture was displayed in the end. Where is my general thinking mistake here?
Thanks everybody.
if (picPath != "")
{
string[] pollPicList = Directory.GetFiles(picPath);
if (pollPicList.Length > 0)
{
for (int i = 0; i < pollPicList.Length; i++)
{
currImageFilename = pollPicList[i];
PollPic = new BitmapImage(new Uri(currImageFilename));
this.Dispatcher.BeginInvoke(new Action(() => ShowDetectPic(PollPic)));
System.Threading.Thread.Sleep(500);
}
}
}
Unsuccessfully tried to use Task.Run instead.
Also not working Task t1 = new Task(() => ShowDetectPic(PollPic));
You can't sleep the GUI thread or nothing will render. Your async code needs to be the waiting, not whatever else it is you're doing there.
async ... Function()
{
string[] pollPicList = Directory.GetFiles(picPath);
if (pollPicList.Length > 0)
{
for (int i = 0; i < pollPicList.Length; i++)
{
currImageFilename = pollPicList[i];
PollPic = new BitmapImage(new Uri(currImageFilename));
ShowDetectPic(PollPic);
await Task.Delay(500);
}
}
}
I am trying to update a ProgressBar from a For loop, in my head, should be easy but I'm struggling a bit.
I've got a Dispatcher method which only seems to fire off twice. Once after the first loop, then not until it has completed. I.e. the loop count should be 6, so each increment would be 16.666667. The ProgressBar will update once to 16.666667 then will wait until the loop has complete to reach 100.
I've read through a few different questions on here, but can't find the answer. I'm still relatively new to this, so any help explained in the dumbest way possible would be appreciated :)
double progressIncrease = (double)100 / TodaysFixtures.Count;
ProcessingBar.Value = 0;
for (int fixture = 0; fixture < TodaysFixtures.Count; fixture++)
{
// ...
await UpdateProcessingBarAsync(progressIncrease);
}
private async Task UpdateProcessingBarAsync(double progressIncrease)
{
await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
ProcessingBar.Value += progressIncrease;
});
}
I'm having a problem.
So I've built an app which displays data in the form of chart and a datagridview. They are both responsive. That means they rescale and move with the data. It takes some computation power I guess.
At the same time I have timers cause it all runs periodically with f=4Hz.
And now: When I run the app and switch on the periodical readout the app hangs during resizing. How could I prevent it?
I've already tried to use a backgroundworker, but the problem occurs in the moment of accessing to the datagridview and chart which are declared (and also used) in the "other thread" (as the VS said)
So.. How could I prevent it?
Maybe I should utilise the backgroundworker in the other way?
My attempts with the backgroundworker:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//Control.CheckForIllegalCrossThreadCalls = false;
if (!GetConnectionStatus())
{
stop_ticking();
if (MessageBox.Show("Device not connected", "Connection status", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error) == DialogResult.Retry)
messaging();
else
return;
}
// TEMP READ
Read_temp(tlist);
float[] t = new float[3];
float[] r = new float[3];
float[] av = new float[1];
float[] st = new float[1];
// TEMP IMPORT
tlist.Give_current_temp(t, r, av, st);
string time_stamp = tlist.Give_current_time();
rows_nr++;
// ADDING TO GRID
dataGridView1.Invoke(new Action(() => { dataGridView1.Rows.Add(new object[] { rows_nr, time_stamp, av[0], st[0], (t[0]).ToString(), (r[0]).ToString(), (t[1]).ToString(), (r[1]).ToString(), (t[2]).ToString(), (r[2]).ToString() }); }));
//dataGridView1.Rows.Add(new object[] { rows_nr, time_stamp, av[0], st[0], (t[0]).ToString(), (r[0]).ToString(), (t[1]).ToString(), (r[1]).ToString(), (t[2]).ToString(), (r[2]).ToString() });
dataGridView1.Invoke(new Action(() => { dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.RowCount - 1; }));
//dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.RowCount - 1;
// ADDING TO CHART
for (int i = 0; i < 3; i++)
chart1.Invoke(new Action(() => { chart1.Series[series_names[i]].Points.AddXY((rows_nr), (t[i])); }));
//chart1.Series[series_names[i]].Points.AddXY((rows_nr), (t[i]));
chart1.Invoke(new Action(() => { chart1.Series["average"].Points.AddXY((rows_nr), (av[0])); }));
//chart1.Series["average"].Points.AddXY((rows_nr), (av[0]));
//chart1.Series["std1"].Points.AddXY((rows_nr), (av[0] + Math.Abs(st[0])));
//chart1.Series["std2"].Points.AddXY((rows_nr), (av[0] - Math.Abs(st[0])));
// MOVING CHART
if (chart1.Series[series_names[0]].Points.Count > nr_of_noints_graph)
{
for (int i = 0; i < 3; i++)
chart1.Series[series_names[i]].Points.RemoveAt(0);
chart1.Series["average"].Points.RemoveAt(0);
//chart1.Series["std1"].Points.RemoveAt(0);
//chart1.Series["std2"].Points.RemoveAt(0);
chart1.ChartAreas[0].AxisX.Minimum = rows_nr - (nr_of_noints_graph - 1);
chart1.ChartAreas[0].AxisX.Maximum = rows_nr;
dataGridView1.Rows.RemoveAt(0);
}
chart1.Invoke(new Action(() => { chart1.ChartAreas[0].RecalculateAxesScale(); }));
//chart1.ChartAreas[0].RecalculateAxesScale();
}
Please take a look at background worker sample. You are doing it wrong. Background worker DoWork should not call UI controls and is executed in non UI thread, it should execute time consuming computing and call worker.ReportProgress(). While ReportProgress method can access UI controls and code in this method is executed in UI thread.
Some chart controls are lugging when adding/removing points. Maybe it hangs because it lugs. Make updates less frequently (1 in 1 second for example) and see whether it hangs or not.
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker?view=netframework-4.7.2
Wrap operations in Stopwatch and use System.Diagnostics.Debug.WriteLine to trace execution flow and time spent on the operations.
Moving chart part does not work because it accesses UI elements in non ui thread without Invoke to UI thread.
If it was not Background worker I would write it this way:
// MOVING CHART
chart1.Invoke(new Action(()=>
{
if (chart1.Series[series_names[0]].Points.Count > nr_of_noints_graph)
{
for (int i = 0; i < 3; i++)
chart1.Series[series_names[i]].Points.RemoveAt(0);
chart1.Series["average"].Points.RemoveAt(0);
chart1.ChartAreas[0].AxisX.Minimum = rows_nr - (nr_of_noints_graph - 1);
chart1.ChartAreas[0].AxisX.Maximum = rows_nr;
}
}
));
I wouldn't wrap each operation in separate Invokes as well.
As for your question it's insufficient information to detect what is wrong please provide minimum viable runnable sample which demonstrates the problem.
As #Access Denied states you should improve separation between GUI and Background worker threads. You could execute // TEMP READ and // TEMP IMPORT operations on background thread and make a call to the GUI thread via .Invoke method when all the data is ready. Read "How to: Make Thread-Safe Calls to Windows Forms Controls" article for more information.
When you add/update data in your DataGridView use .BeginUpdate/.EndUpdate methods to prevent control update until all the data is refreshed.
Other approach is to use Virtual mode. It's especially usefull if you have many items in grid.
When working with a background thread you must not create, update, or even access any UI element.
You need to separate the work that retrieves your data (the slow part) from the work that updates the chart (which is very fast).
It really comes down to doing it like this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (!GetConnectionStatus())
{
stop_ticking();
return;
}
// TEMP READ
Read_temp(tlist);
float[] t = new float[3];
float[] r = new float[3];
float[] av = new float[1];
float[] st = new float[1];
// TEMP IMPORT
tlist.Give_current_temp(t, r, av, st);
string time_stamp = tlist.Give_current_time();
rows_nr++;
chart1.Invoke(new Action(() =>
{
// ADDING TO GRID
dataGridView1.Rows.Add(new object[] { rows_nr, time_stamp, av[0], st[0], (t[0]).ToString(), (r[0]).ToString(), (t[1]).ToString(), (r[1]).ToString(), (t[2]).ToString(), (r[2]).ToString() });
dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.RowCount - 1;
// ADDING TO CHART
for (int i = 0; i < 3; i++)
{
chart1.Series[series_names[i]].Points.AddXY((rows_nr), (t[i]));
}
chart1.Series["average"].Points.AddXY((rows_nr), (av[0]));
// MOVING CHART
if (chart1.Series[series_names[0]].Points.Count > nr_of_noints_graph)
{
for (int i = 0; i < 3; i++)
{
chart1.Series[series_names[i]].Points.RemoveAt(0);
}
chart1.Series["average"].Points.RemoveAt(0);
chart1.ChartAreas[0].AxisX.Minimum = rows_nr - (nr_of_noints_graph - 1);
chart1.ChartAreas[0].AxisX.Maximum = rows_nr;
dataGridView1.Rows.RemoveAt(0);
}
chart1.ChartAreas[0].RecalculateAxesScale();
}));
}
If you have to show a MessageBox then you also need to invoke that.
User can perform an action and I add data to a collection that is bound to a DataGrid and that works fine.
But when there is a long running process that has multiple records added to the collection I never see the DataGrid updated until the process is over. What am I doing wrong? I am really new to this so I am sure the issue is something simple.
private ObservableCollection<StatusEntry> _collSe = new ObservableCollection<StatusEntry>();
public ObservableCollection<StatusEntry> CollSe
{
get { return _collSe; }
set
{
_collSe = value;
// NotifyPropertyChanged("CollSe");
}
}
CollSe.Add(new StatusEntry() { TimeStamp = DateTime.Now, Comment = "STARTED: Translating file to DataTable" });
DataTable dt = ExcelHelper.ReadAsDataTable(tbFileName.Text);
CollSe.Add(new StatusEntry() { TimeStamp = DateTime.Now, Comment = "COMPLETE: Translating file to DataTable" });
EDIT:
More clearly here is what I tried.. Still does not update the UI until the end though
private void btnProcessFile_Click(object sender, RoutedEventArgs e)
{
//THis should happen as soon as the button is pressed
ThreadStart job = new ThreadStart(() =>
{
for (int i = 0; i < 20; i++)
{
// The new thread puts UI operations in the dispatching queue
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
CollSe.Add(new StatusEntry() { TimeStamp = DateTime.Now, Comment = "Happy Tools STARTED" });
}));
}
});
Thread thread = new Thread(job);
thread.Start();
//The another minute of processing here.....
I made the mistake of asking a question without understanding the basics and therefore coding all over the place not sure what I was doing. So I think I got it now. The article that helped my simple mind understand it is here..
The main point is to understand WPF has a main thread and a UI thread and how to go from one to the other to allow access to the UI.
http://elegantcode.com/2009/07/03/wpf-multithreading-using-the-backgroundworker-and-reporting-the-progress-to-the-ui/
My problem is a synchronization problem with a thread and the user simultaneously accessing and modifying a LinkedList.
I’m making a program in C# that will display some messages in a panel. I’m getting an error called “The collection was modified after the enumerator was instantiated.”, that is because I’m adding or removing messages while a thread is accessing the LinkedList.
I have read some solutions but I am still unable to make them work. I’m using an Enumerator for the thread work in my LinkedList. I tried to make some locks in my code so the thread would not iterate the list while I remove or add an element. I also tried to lock the thread for the operations on my list. But all my attempts failed.
Here is some code of my project. This one is for adding a message:
public void addMsg(MsgForDisplay msg) {
Label lbl = new Label();
lbl.Text = (msg.getMsgText() + " -");
lbl.ForeColor = color;
lbl.Font = textFont;
lbl.BackColor = backg;
lbl.Visible = true;
lbl.AutoSize = true;
lbl.Location = new Point(width(), 0);
//lock(labels) { tried to lock here but failed
labels.AddLast(lbl);
lastLb = lbl;
this.Controls.Add(lbl);
this.Refresh();
//}
}
Removing a message:
public void removeMsg(string msg) {
string remove = msg + " -";
Label lbRemove = null;
//lock(labels) { also tried to lock here
var it = labels.GetEnumerator();
while(it.MoveNext()) {
Label label = it.Current;
if (label.Text.Equals(remove)) {
lbRemove = label;
}
}
labels.Remove(lbRemove);
this.Controls.Remove(lbRemove);
this.Refresh();
//}
}
And there is the problem, in my thread:
public void run() {
while (true) {
// lock (labels) { also tried to lock here
var it = labels.GetEnumerator();
while (it.MoveNext()) { // the crash occurs here
Label lb = it.Current;
if (lb.Location.X + lb.Width < 0) {
this.Invoke(new MethodInvoker(() => { this.Controls.Remove(lb); }));
if (labels.Count > 1)
this.Invoke(new MethodInvoker(() => { lb.Location = new Point(lastLb.Right, 0); }));
else
this.Invoke(new MethodInvoker(() => { lb.Location = new Point(2000, 0); }));
lastLb = lb;
this.Invoke(new MethodInvoker(() => { this.Controls.Add(lb); }));
this.Invoke(new MethodInvoker(() => { this.Refresh(); }));
}
if (leftLb != null)
if (leftLb.Location.X + leftLb.Width - lb.Location.X < -20)
this.Invoke(new MethodInvoker(() => { lb.Location = new Point(leftLb.Right, 0); }));
else
this.Invoke(new MethodInvoker(() => { lb.Location = new Point(lb.Location.X - 3, lb.Location.Y); }));
leftLb = lb;
}
System.Threading.Thread.Sleep(30);
// }
}
}
As you can see I’m using an GetEnumerator of my labels, what in Java should be the Iterator. With this I shouldn’t be able to iterate the list without problem when the user add or remove messages?
Is there a way to synchronize the accesses to the list?
EDIT: I have tried the ConcurrentBag and ConcurrentDictionary but without any improvement to the project as you can see in the comments…
Please before you post an answer read the comments bellow to make sure that you know what is going on.
EDIT: Tried to add a mutex to my code for addMsg and removeMsg but still crashing. If I use the mutex in the thread it will be slowed down.
I created a Timer in step of the thread and that solved the crashing problem. Here is the code if you want to see it.
private System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
private void startThread() {
myTimer.Tick += new EventHandler(timerEvent);
myTimer.Interval = 30;
myTimer.Start();
}
private void timerEvent(object sender, EventArgs e) {
var it = labels.GetEnumerator();
while (it.MoveNext()) {
Label lb = it.Current;
// Label lb = labels.ElementAt(b);
if (lb.Location.X + lb.Width < 0) {
this.Controls.Remove(lb);
if (labels.Count > 1)
lb.Location = new Point(lastLb.Right, 0);
else
lb.Location = new Point(2000, 0);
lastLb = lb;
this.Controls.Add(lb);
this.Refresh();
}
if (leftLb != null)
if (leftLb.Location.X + leftLb.Width - lb.Location.X < -20)
lb.Location = new Point(leftLb.Right, 0);
else
lb.Location = new Point(lb.Location.X - 3, lb.Location.Y);
leftLb = lb;
}
}
The source of your problem is that while you are iterating over the list of labels You call either Remove or Add functions which modifies this list whis is not allowed while iterating over it. Instead of this
var it = labels.GetEnumerator();
while (it.MoveNext()) // the crash occurs here
I suggest something like that:
for(int i = 0; i < labels.Count; i++)
{
labels.remove(labels[i]); //this is valid of course the count of the list will change
//Here you can add or remove elements from the labels
}
Or you can try first to collect the removable items into a temporal list and later remove it from the original.
As others have already stated, the problem is you are modifying the collection while enumerating over it.
Now, the easiest workaround is obviously not to enumerate over the same collection that is being modified. And how do you do that? Simple, you just clone the collection, and iterate over it:
lock (labels)
{
var clone = new LinkedList<Label>(labels);
it = labels.GetEnumerator();
}
Now you can enumerate over it safely, without worrying about inconsistencies.
A few notes:
I am using a lock, because the cloning also must enumerate over your collection, and while it does it in a very short time, it is still required for synchronization. Off course, you need to uncomment the locks you've already added to addMsg and removeMsg.
The reason that locking your whole loop didn't work, is that when you call Invoke, you are essentially returning control to the thread that owns the object (the main GUI thread in this case). The problem is, that this thread is already stuck on handling whatever event caused addMsg or removeMsg to be called, leading to a deadlock.
You should also note that cloning a collection every 30 ms, isn't exactly efficient, and shouldn't be used in a production code, but given that this probably just an exercise, it should suffice. In real life, you should probably use a separate collection for the changes you are about to do (adding or removing labels), change this collection in addMsg and removeMsg, and then merge the changes to labels inside your thread, but outside of the iteration over the labels.
Not directly related to your question, but still: you should use a foreach loop instead of directly creating an enumerator object in C#.
As stated before, changing any collection while enumerating it, results in an exception in .Net. You can avoid this by using for or while loops.
However I don't see the point in using a Linked List in this scenario. It should be way simpler and more performant to use a ConcurrentDictionary and just add or remove the requested item. There is also a ObservableConcurrentDictionary available, although not part of the Framework. It is very stable, in my experience.
http://www.codeproject.com/Articles/208361/Concurrent-Observable-Collection-Dictionary-and-So