I’ve created a user control. In the control I have two methods – sending and receiving some data. Those methods also update the data grid located on the user control.
public void RunTX()
{
tx_run = new Thread(new ThreadStart(SendCanFrames));
if (!tx_run.IsAlive)
{
tx_run.IsBackground = true;
tx_run.Start();
}
}
public void RunRX()
{
rx_run = new Thread(new ThreadStart(ReadCanFrames));
if (!rx_run.IsAlive)
{
rx_run.IsBackground = true;
rx_run.Start();
}
}
private void ReadCanFrames()
{
ushort prev_time_stamp = 0;
while (running)
{
if (CanDevice != null)
CanDevice.Read(ref rx_can_msg, 1, ref read_cnt);
if (read_cnt == 1)
{
read_cnt = 0;
dataGridViewCanRx.Rows[0].Cells[0].Value = rx_can_msg[0].Id.ToString("X");
dataGridViewCanRx.Rows[0].Cells[1].Value = rx_can_msg[0].Size.ToString();
dataGridViewCanRx.Rows[0].Cells[2].Value = BytesToString(rx_can_msg[0].Data);
dataGridViewCanRx.Rows[0].Cells[3].Value = (rx_can_msg[0].TimeStamp - prev_time_stamp).ToString();
prev_time_stamp = rx_can_msg[0].TimeStamp;
}
prev_time_stamp = rx_can_msg[0].TimeStamp;
}
}
private void SendCanFrames()
{
if (tx_can_msg.Length == 0) return;
VSCAN_MSG[] l_msgs = new VSCAN_MSG[2];
while (running)
{
for (int i = 0; i < tx_can_msg.Length; i++)
{
if (can_messages[i].CountRun < can_messages[i].CountMax)
{
can_messages[i].TimeStamp1 = DateTime.Now;
interval = can_messages[i].TimeStamp1 - can_messages[i].TimeStamp2;
if (interval.TotalMilliseconds >= can_messages[i].Period)
{
can_messages[i].TimeStamp2 = DateTime.Now;
l_msgs[0] = tx_can_msg[i];
//send CAN frame
CanDevice.Write(l_msgs, 1, ref written_cnt);
// send immediately
CanDevice.Flush();
can_messages[i].CountRun++;
dataGridViewCanTx.Rows[i].Cells[4].Value = can_messages[i].CountRun.ToString();
}
}
}
}
}
I put four user control instances on the main form and start all four controls.
private void buttonStartAll_Click(object sender, EventArgs e)
{
int can_channel;
for (can_channel = 0; can_channel < 4; can_channel++)
{
if (started[can_channel] == false)
{
if (connected[can_channel] == true)
{
switch (can_channel)
{
case 0:
mainform.userControlCan1.RunTX();
mainform.userControlCan1.RunRX();
started[can_channel] = true;
break;
case 1:
mainform.userControlCan2.RunTX();
mainform.userControlCan2.RunRX();
started[can_channel] = true;
break;
case 2:
mainform.userControlCan3.RunTX();
mainform.userControlCan3.RunRX();
started[can_channel] = true;
break;
case 3:
mainform.userControlCan4.RunTX();
mainform.userControlCan4.RunRX();
started[can_channel] = true;
break;
}
}
}
}
}
All is running, however the GUI freezing and data grids updated by jumps. Why?
I recomend you to look at two key points: Invoke and Application.DoEvents.
First one will help you to update your GUI in main thread instead of child threads.
Have a look at : https://learn.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.invoke?view=netframework-4.8
Application.DoEvents() method tells the system to do other waiting jobs without blocking in some loops. Otherwise updating GUI jobs are postponed and GUI is blocked. Here is the information about this method: https://learn.microsoft.com/tr-tr/dotnet/api/system.windows.forms.application.doevents?view=netframework-4.8
private delegate void dlgUpdateRows(object[] rx_can_msg, int tID);
// Write actual type of rx_can_msg instead of object[] in method signature , second parameter should be your thread id if needed
private void UpdateRows(object[] rx_can_msg, int tID =0)
{
try
{
if (this.InvokeRequired)
{
object[] obj = new object[2];
obj[0] = rx_can_msg;
obj[1] = Thread.CurrentThread.ManagedThreadId;
this.Invoke(new dlgUpdateRows(UpdateRows), obj);
}
else
{
//Here update your datagrid using rx_can_msg
}
//This row is important to avoid blocking
Application.DoEvents();
}
catch (Exception ex)
{
//Do error handling
}
}
Related
I've a basic form application. It displays a list of checkbox and a list of asyn task update CheckBox label :
public partial class Form1 : Form
{
List<CheckBox> checkedBoxes = new List<CheckBox>();
public Form1()
{
InitializeComponent();
CreateCheckBoxes();
Display();
}
public void CreateCheckBoxes()
{
SuspendLayout();
for (int i = 0; i < 11; i++)
{
checkedBoxes.Add(new CheckBox() {Name = "Combo"+i, Text = "-"+i });
flowLayoutPanel1.Controls.Add(checkedBoxes[i]);
}
ResumeLayout();
}
private async void Display()
{
List < Task < LyStruc >> tasks = new List<Task<LyStruc>>();
for (int i = 0; i < 10; i++)
{
tasks.Add(Task.Run(() => { return ProcessMessage(i); }) );
}
var wa = await Task.WhenAll(tasks.ToArray());
// Doesn't work (few checkbox update label)
foreach (LyStruc t in wa)
{
UpdateCheckBoxLabel(t);
}
// Doesn't work either.
//TaskScheduler taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
//foreach (var item in tasks)
//{
// item.ContinueWith(c =>
// {
// switch (c.Status)
// {
// case TaskStatus.RanToCompletion: Test(c.Result); break;
// case TaskStatus.Faulted: richTextBox1.Text = c.Exception.ToString(); break;
// }
// }, taskScheduler);
// }
}
private void UpdateCheckBoxLabel(LyStruc s)
{
this.checkedBoxes[s.Id].Text = "Update" + s.Name;
}
private LyStruc ProcessMessage(int i)
{
return new LyStruc()
{
Id = i,
Name = string.Format( "-Combo {0}", i),
};
}
}
Why don't all checkboxes change (just few of them) ? I get no errors. I expect that each check box update with label CheckBox
The program uses two threads: one for collecting the data from USB, and the other writes/displays the data. It seems to work as expected, however, as soon as I minimize/restore any windows on the PC, or press any buttons on the Form, it stops working properly (it stops receiving data). I use link list to keep the data streaming in order. I suspect that maybe the link is lost somewhere because of improper variable declaration. Is there an obvious mistake in my code that could be causing this?
namespace Streamer
{
public class Form1 : System.Windows.Forms.Form
{
Thread tListen;
Thread tDisplay;
public class MY_DATA_BUFFER
{
public byte[] buffer;
public int length;
public MY_DATA_BUFFER nextBuff;
}
private Object thisLock = new Object();
MY_DATA_BUFFER pHead = null;
private static AutoResetEvent DataQueueEvent = new AutoResetEvent(false);
public Form1()
{
// some settings;
}
public unsafe void dataDisplayThread()
{
MY_DATA_BUFFER pWorkingSet = pHead;
if (pWorkingSet == null)
{
// Make sure link list head is initialized...........
do {DataQueueEvent.WaitOne();} while (pHead == null);
// Wait till the two datas are available to write.
if (pHead.nextBuff == null){ DataQueueEvent.WaitOne(); }
if (pWorkingSet == null) { pWorkingSet = pHead; }
}
// Let's start the data write loop
while (bRunning || (pWorkingSet != null))
{
// copy small array to a bigger array
for (int i = 0; i < pWorkingSet.length; i++)
{
pixelValues[pixptr] = pWorkingSet.buffer[i];
pixptr++;
}
if (pixptr >= imWidth * imHeight)
{
pixptr = 0;
// show data in pixelValues
}
// Traverse through the link list data structure.
if (pWorkingSet.nextBuff == null)
{
do
{
if (pWorkingSet.nextBuff == null DataQueueEvent.WaitOne();
lock (thisLock)
{
if (pWorkingSet.nextBuff == null && !bRunning)
break;
}
} while (pWorkingSet.nextBuff == null);
}
// We are good to loop for the next operation
lock (thisLock)
{
pHead = pHead.nextBuff;
pWorkingSet = pHead;
}
}
// All write operation is at completion
pHead = null;
}
public unsafe void XferData(...)
{
MY_DATA_BUFFER tempBuff = null;
for (; bRunning; )
{
// .... do something
// push the data to a link list for
lock (thisLock)
{
MY_DATA_BUFFER newBuff = new MY_DATA_BUFFER();
newBuff.nextBuff = null;
newBuff.buffer = xBufs[k];
newBuff.length = len;
if (tempBuff == null) tempBuff = newBuff;
else
{
tempBuff.nextBuff = newBuff;
tempBuff = newBuff;
}
if (pHead == null) pHead = newBuff;
else
{
DataQueueEvent.Set();
}
}
///////////////////Link List Population completes///////////
Thread.Sleep(0);
} // End infinite loop
}
private void StartBtn_Click(object sender, System.EventArgs e)
{
if (StartBtn.Text.Equals("Start"))
{
// ...
pHead = null;
bRunning = true;
tListen = new Thread(new ThreadStart(XferThread));
tListen.IsBackground = true;
tListen.Priority = ThreadPriority.Highest;
tListen.Start();
tDisplay = new Thread(new ThreadStart(dataDisplayThread));
tDisplay.IsBackground = true;
tDisplay.Priority = ThreadPriority.Highest;
tDisplay.Start();
}
else
{
if (tListen.IsAlive)
{
// ...
bRunning = false;
if (tListen.Join(5000) == false )
tListen.Abort();
tListen = null;
}
if (tDisplay.IsAlive)
{
if (tDisplay.Join(5000) == false )
tDisplay.Abort();
Display = null;
}
}
}
}
}
I have something like a demon function, it's started on new thread, and it is an endless loop that checks for condition, if true it updates textbox, it sleaps some time, and so, again and again. The function I use to update the textbox is this:
private static void pour(TextBox t, string s)
{
try
{
Form f = t.FindForm();
if (t.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(pour);
f.Invoke(d, new object[] { t, s });
}
else
{
if (t.Text == "") t.Text += s;
else t.Text += Environment.NewLine + s;
if (t.Text != "") { t.Select(t.Text.Count() - 2, 1); t.ScrollToCaret(); }
}
}
catch { }
}
The problem is, the demon will invoke pour on the main thread and continue before the textbox is updated, thus invoking pour few times, that results in the textbox being updated few times with the same text, instead of once. How to wait for this invoked method to finish?
edit:
This is the demon function:
private void demon_drain(string name, int interval)
{
System.Threading.Thread drain = new System.Threading.Thread(() =>
{
try
{
while (true)
{
try
{
TextBox t = new TextBox();
try
{
t = (TextBox)F.Controls[name];
}
catch { System.Threading.Thread.Sleep(interval); continue; }
if (Drain == null) { System.Threading.Thread.Sleep(interval); continue; }
if (Drain == t.Text) { System.Threading.Thread.Sleep(interval); continue; }
pour(t, Drain);
System.Threading.Thread.Sleep(interval); continue;
}
catch { System.Threading.Thread.Sleep(interval); continue; }
}
}
catch { }
});
drain.IsBackground = true;
drain.Start();
}
When I set a break point after the pour it works fine, and the shorter the sleep, the more updates are done, thus I think it's not waiting for the invoke, actually...
I have a simple application that executes a function in a separate thread via a BackgroundWorker and am running into issues. I am collecting a string array of values and passing that back to my ProgressChanged event via the ReportProgress method and I'm running into a problem where the thread continues and is so fast, it outruns the ProgressChanged event and overwrites the values before they can be added to a grid. Below is my code...
Button Click Event Fires my thread...
private void LoadButton_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(WorkingPathTextBox.Text))
{
this.dataGridView1.Rows.Clear();
this.progressBar1.Visible = true;
this.LoadButton.Visible = false;
this.BrowseButton.Enabled = false;
this.WorkingPathTextBox.Enabled = false;
this.CancelBtn.Visible = true;
this.ProcessingLabel.Visible = true;
beginTime = DateTime.Now;
WorkflowCleanup wc = new WorkflowCleanup();
wc.WorkflowPath = this.WorkingPathTextBox.Text;
backgroundWorker1.RunWorkerAsync(wc);
}
}
In the DoWork, it loads the class where my work exists...
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
System.ComponentModel.BackgroundWorker worker;
worker = (System.ComponentModel.BackgroundWorker)sender;
// Get the Words object and call the main method.
WorkflowCleanup wc = (WorkflowCleanup)e.Argument;
wc.LoadAssemblies(worker, e);
wc.LoadDataDefinitions(worker, e);
wc.LoadDataDefinitionsDirty(worker, e);
wc.LoadProcesses(worker, e);
wc.LoadProcessesDirty(worker, e);
wc.LoadWorkflows(worker, e);
// wc.UpdateWorkflows(worker, e);
}
WorkflowCleanup class...the ReportProgress in stuck right in the middle of the try/catch because I could not figure out what was causing the problem. So, I moved it in the try, and even had to add a Thread.Sleep(100) to slow the process down to give the ProgressChanged event time enough to add the row of data I passed it to the grid before it was overridden...
namespace WorkflowMaintenance
{
public class WorkflowCleanup
{
private int errorCount = 0;
private int fixCount = 0;
public class Workflow
{
// code truncated
}
public class WorkflowAssembly
{
// code truncated
}
public class DataDefinition
{
// code truncated
}
public class CurrentState
{
public int Percentage;
public string StateString;
public List<string[]> ProcessResults;
public string[] Result;
public int ErrorCount;
public int FixCount;
}
public void LoadAssemblies(System.ComponentModel.BackgroundWorker worker, System.ComponentModel.DoWorkEventArgs e)
{
CurrentState state = new CurrentState();
fixCount = 0;
errorCount = 0;
string[] asmbfiles = System.IO.Directory.GetFiles(WorkflowPath + "\\Assemblies", "*.asmb", System.IO.SearchOption.AllDirectories);
int asmbIndex = 0;
Assemblies = new List<WorkflowAssembly>();
// Assemblies (NEW)
foreach (string asmb in asmbfiles)
{
if (worker.CancellationPending)
{
e.Cancel = true;
break;
}
else
{
//results = new List<string[]>();
asmbIndex += 1;
int percentage = (asmbIndex * 100) / asmbfiles.Length;
state.StateString = string.Format("Loading Assemblies... {0}%", percentage);
state.Percentage = percentage;
try
{
XDocument xdoc = XDocument.Load(asmb);
XElement asmbElement = xdoc.Descendants(wf + "assembly").First();
XElement asmbTypes = asmbElement.Element(wf + "types");
string asmbid = asmbElement.Attribute("id").Value;
string asmbname = asmbElement.Element(wf + "name").Value;
string asmbpath = asmbElement.Element(wf + "assemblyPath").Value;
Assemblies.Add(new WorkflowAssembly() { ID = asmbid, Name = asmbname, FileName = asmb, AssemblyPath = asmbpath, Types = asmbTypes });
//results.Add(new string[] { "SUCCESS", "ASSEMBLY PROCESSED SUCCESSFULLY", asmbname, asmbid, null, asmb });
state.Result = new string[] { "SUCCESS", "ASSEMBLY PROCESSED SUCCESSFULLY", asmbname, asmbid, null, asmb };
fixCount += 1;
//if (results != null && results.Count > 0)
//{
// state.ProcessResult = results;
//}
state.FixCount = fixCount;
state.ErrorCount = errorCount;
worker.ReportProgress(percentage, state);
Thread.Sleep(100);
}
catch (Exception)
{
// need to report the exception...
errorCount += 1;
}
finally
{
//if (results != null && results.Count > 0)
//{
// state.ProcessResult = results;
//}
//state.FixCount = fixCount;
//state.ErrorCount = errorCount;
//worker.ReportProgress(percentage, state);
}
}
}
}
}
}
ProgressChanged Event...
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// This method runs on the main thread.
WorkflowCleanup.CurrentState state = (WorkflowCleanup.CurrentState)e.UserState;
this.progressBar1.Value = state.Percentage;
this.ProcessingLabel.Text = state.StateString;
this.errorLabel.Text = string.Format("Errors: {0}", state.ErrorCount);
this.fixLabel.Text = string.Format("Fixed: {0}", state.FixCount);
this.ElapsedTimeLabel.Text = string.Format("Elapsed Time: {0}", GetProgressElapsedString());
if (state.Result != null)
{
this.dataGridView1.Rows.Add(state.Result);
}
}
Problem...the problem is that if I have 3 files in the folder (named Assembly1, Assembly2, and Assembly3), the output in the grid shows that all three are named Assembly3. The only way I could fix this was to tell the Thread to sleep. This is not an option as I have to run this for a folder that has approximately 18,000 files and having to sleep for a millisecond would take forever. BUT, I need to get valid results. Help please :-)
Rather than creating the state object outside the loop and mutating it in each iteration, create a new instance for each iteration. This can be done by simply moving the declaration of the variable inside the loop.
Boys and Girls,
I got this method (task) that gets executed when I select a node in a treeview. It retreives data from a database and puts in a ReportControl (Codejock).
What I need is to prevent that this method (task) gets executed again while it is still running.
I've been experimenting with booleans set to false when starts and set to true if finishes but that doesn't work for some reason.......
here is the code:
the event where the method gets executed:
private void tvObjects_AfterSelect(object sender, TreeViewEventArgs e)
{
try
{
tvObjects.PreseveTreeState = true;
tvObjects.SaveTreeState();
tvObjects.SelectedNode.BackColor = Color.FromArgb(191, 210, 234);
AllowPreview = false;
WordPreviewer.UnloadPreviewHandler();
viewer1.Image = null;
rcDocumenten.ClearContent();
rcEmail.ClearContent();
var n = e.Node as ExtendedTreeNode;
tvObjects.CurrentNode = e.Node;
SelectedObjectNode = n;
WordPreviewer.FileName = null;
if (n != null)
{
Document.SetDossierNummer(n.DossierNr);
}
var selNode = e.Node as ExtendedTreeNode;
if (selNode != null && selNode.DossierNode)
{
if (selNode.IsFolder)
{
DossierNr = Convert.ToInt32(selNode.DossierNr);
SelectedObjectNode = selNode;
var col = new col();
col.CreateCurrentDossierDocumentsList(Convert.ToInt32(selNode.DossierNr.ToString()),
selNode.Tag.ToString());
col.CreateCurrentEmailList(selNode.DossierNr, Convert.ToInt32(selNode.Tag.ToString()));
var t =
new Thread(
() =>
rcDocumenten_Populate(Convert.ToInt32(selNode.DossierNr.ToString()),
selNode.Tag.ToString()));
t.Start();
var t2 = new Thread(
() => rcEmail_Populate(selNode.DossierNr, Convert.ToInt32(selNode.Tag.ToString())));
t2.Start();
tcDocumenten.SelectedTab = selNode.Text.Contains("Email") ? tpEmail : tpDocumenten;
}
else
{
tpDocumenten.Text = #"Documenten (0)";
tpEmail.Text = #"Emails (0)";
SelectedBestandId = -1;
SelectedBestandsNaam = string.Empty;
SelectedEmailId = -1;
SelectedEmailOnderwerp = string.Empty;
}
}
else if (selNode != null && selNode.PersonalNode)
{
if (!selNode.IsMedewerker)
{
var t =
new Thread(
() => rcDocumenten_PersoonlijkeMappenPopulate(Convert.ToInt32(selNode.Tag.ToString())));
t.Start();
}
}
}
catch (InvalidOperationException iex)
{
MessageBox.Show(iex.ToString());
}
catch (Exception ex)
{
var dmsEx = new DmsException("Fout tijdens het uitvoeren event AfterSelect tvObjects ", "VDocumenten (tvObjects Event: AfterSelect)", ex);
ExceptionLogger.LogError(dmsEx);
}
}
the method that should not run twice:
public void rcDocumenten_PersoonlijkeMappenPopulate(int personalFolderId)
{
try
{
AllowPreview = false;
var oc = new col();
rcDocumenten.FocusedRow = null;
oc.CreateCurrentPersoonlijkeDocumentsList(personalFolderId);
UpdateUI(false);
if (rcDocumenten.InvokeRequired)
{
rcDocumenten.Invoke((MethodInvoker)delegate
{
rcDocumenten.Records.DeleteAll();
rcDocumenten.Redraw();
_gegevensLaden = new GegevensLaden(this);
_gegevensLaden.Show();
//Documenten uit Database ophalen
_gegevensLaden.progressbar.Maximum = col.ListPersoonlijkeDocuments.Count;
foreach (var document in col.ListPersoonlijkeDocuments)
{
var versie = Convert.ToDecimal(document.Versie.ToString());
if (document.OriBestandId == 0)
{
//Record toevoegen
rcDocumenten_Persoonlijk_AddRecord(document.BestandId, document.BestandsNaam, versie,
document.DatumToevoeg, document.DatumUitcheck, document.UitgechecktDoor, document.Eigenaar,
document.DocumentType, document.DocumentProgres);
}
_gegevensLaden.progressbar.Value = _gegevensLaden.progressbar.Value + 1;
_gegevensLaden.progressbar.Update();
}
var aantalRecords = 0;
for (var i = 0; i < rcDocumenten.Records.Count; i++)
{
aantalRecords++;
for (var j = 0; j < rcDocumenten.Records[i].Childs.Count; j++)
{
aantalRecords++;
}
}
tpDocumenten.Text = #"Documenten (" + aantalRecords + #")";
rcDocumenten.Populate();
Invoke(new UpdateUIDelegate(UpdateUI), new object[] { true });
});
}
//"dd-MM-yyyy HH:mm:ss"
AllowPreview = true;
}
catch (Exception ex)
{
var dmsEx = new DmsException("Fout bij de populatie van Report Control", "VDocumenten (rcDocumenten_Persoonlijk_Populate)", ex);
ExceptionLogger.LogError(dmsEx);
}
}
You can check if the thread/task has completed. Change the thread creation to use a Task
_t =
Task.Factory.StartNew(
() =>
rcDocumenten_Populate(Convert.ToInt32(selNode.DossierNr.ToString()),
selNode.Tag.ToString()));
Then you can keep the Task around in the class scope. As you see above, I called it _t.
private Task _t; // documenten vullen achtergrond thread
Now, instead of blindly starting the Task, check if the Task should be started.
if (_t == null || _t.IsCompleted) {
That would solve your current issue.