I have a function which modifies my listview "inputList",the listview has 5 columns. The function is supposed to check the cells in the listview,the cells in the last column will have "NORMAL" with green background if there is nothing wrong and if there is something wrong, it will have "ERROR" with red background in the cell in the last column and also change the color of the cells that are wrong in the same line from the other columns.
There are 4k items, I know that it's pointless to have so many item since the user is not going to read them but I was asked to this way.
I'm using a function with backgroundworker but its not fast enough and thought parallelFor would be faster. But when I try the parallelFor, its freezing the program.
This is the function with backgroundworker which is working but its too slow:
private void bw3_DoWork(object sender, DoWorkEventArgs e)
{
String tempTrain = "";
String tempPredic = "";
String tempNet = "";
Boolean bTrain;
Boolean bPredic;
Boolean bNetErro;
int n;
int nNet = 0;
String tTrain = "";
String tPredic = "";
int nList = 0;
this.Invoke(new MethodInvoker(() => {
nList = inputList.Items.Count;
nNet = menuNetwork.Items.Count;
tTrain = dtrainTextBox.Text;
tPredic = dpredicTextBox.Text;
}));
for (int i = 0; i < nList; i++)
{
ListViewItem.ListViewSubItem temp1 = new ListViewItem.ListViewSubItem();
temp1.BackColor = Color.LightGreen;
temp1.Text = "NORMAL";
this.Invoke(new MethodInvoker(() =>
{
inputList.Items[i].SubItems[0].BackColor = Color.White;
inputList.Items[i].SubItems[1].BackColor = Color.White;
inputList.Items[i].SubItems[2].BackColor = Color.White;
tempTrain = String.Format("{0}\\{1}", tTrain, inputList.Items[i].SubItems[1].Text);
tempPredic = String.Format("{0}\\{1}", tPredic, inputList.Items[i].SubItems[2].Text);
tempNet = (String)inputList.Items[i].SubItems[0].Tag;
}));
bTrain = (!File.Exists(tempTrain));
bPredic = (!File.Exists(tempPredic));
bNetErro = !(int.TryParse(tempNet, out n));
if (!bNetErro)
{
if (!(n < nNet))
{
bNetErro = true;
}
}
this.Invoke(new MethodInvoker(delegate
{
if (bTrain) inputList.Items[i].SubItems[1].BackColor = Color.Red;
if (bPredic) inputList.Items[i].SubItems[2].BackColor = Color.Red;
if (bNetErro) inputList.Items[i].SubItems[0].BackColor = Color.Red;
if (bTrain | bPredic | bNetErro) { temp1.Text = "Erro"; temp1.BackColor = Color.Red; }
try { inputList.Items[i].SubItems[4] = temp1; }
catch (ArgumentOutOfRangeException) { inputList.Items[i].SubItems.Add(temp1); }
}));
}
}
And this is the function with ParallelFor, the program stops working even when I try a very small example with only items.
private void tent1()
{
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = 4;
String tempTrain = "";
String tempPredic = "";
String tempNet = "";
String tTrain = dtrainTextBox.Text;
String tPredic = dpredicTextBox.Text;
Boolean bTrain;
Boolean bPredic;
Boolean bNetErro;
int n;
int nNet = networkList.Items.Count;
Parallel.For(0, inputList.Items.Count,
i =>
{
ListViewItem.ListViewSubItem temp1 = new ListViewItem.ListViewSubItem();
temp1.BackColor = Color.LightGreen;
temp1.Text = "NORMAL";
Console.WriteLine(i);
this.Invoke(new MethodInvoker(() =>
{
inputList.Items[i].SubItems[0].BackColor = Color.White;
inputList.Items[i].SubItems[1].BackColor = Color.White;
inputList.Items[i].SubItems[2].BackColor = Color.White;
tempTrain = String.Format("{0}\\{1}", tTrain, inputList.Items[i].SubItems[1].Text);
tempPredic = String.Format("{0}\\{1}", tPredic , inputList.Items[i].SubItems[2].Text);
tempNet = (String)inputList.Items[i].SubItems[0].Tag;
}));
bTrain = (!File.Exists(tempTrain));
bPredic = (!File.Exists(tempPredic));
bNetErro = !(int.TryParse(tempNet, out n));
if (!bNetErro)
{
if (!(n < nNet))
{
bNetErro = true;
}
}
this.Invoke(new MethodInvoker(delegate
{
if (bTrain) inputList.Items[i].SubItems[1].BackColor = Color.Red;
if (bPredic) inputList.Items[i].SubItems[2].BackColor = Color.Red;
if (bNetErro) inputList.Items[i].SubItems[0].BackColor = Color.Red;
if (bTrain | bPredic | bNetErro) { temp1.Text = "Erro"; temp1.BackColor = Color.Red; }
try { inputList.Items[i].SubItems[4] = temp1; }
catch (ArgumentOutOfRangeException) { inputList.Items[i].SubItems.Add(temp1); }
}));
});
}
How can I copy all items of the listview to a List without reference? I think adding/modify directly into the listview and the invokes are slowing the function so creating: List<ListViewItem> tList
I would modify everything in the tList and then I would use inputList.Items.AddRange(tList.ToArray());
This would remove all the invokes inside the loop.
ListViewItem[] tItemsTemp = null;
this.Invoke(new MethodInvoker(() =>
{
tItemsTemp = new ListViewItem[inputList.Items.Count];
inputList.Items.CopyTo(tItemsTemp, 0);
nList = inputList.Items.Count;
nNet = menuNetwork.Items.Count;
tTrain = dtrainTextBox.Text;
tPredic = dpredicTextBox.Text;
}));
List<ListViewItem> tList = new List<ListViewItem>(tItemsTemp);
ListViewItem[] tItems = (ListViewItem[]) tItemsTemp.Clone();
//Modifies the list or array of listviewitems
this.Invoke(new MethodInvoker(delegate
{
inputList.Items.Clear();
// Just one of them,not the 3,just showing how i would call them.
inputList.Items.AddRange(tItems);
inputList.Items.AddRange(tItemsTemp);
inputList.Items.AddRange(tList.ToArray());
}));
But tItemsTemp,tItems,tList were all references...
How can I copy with creating a reference?
Mixing UI and parallelization is not guaranteed to provide good results. The UI is a bottleneck by itself because it runs on a unique thread. Worse, yours includes some disk IO operations (File.Exists).
There are some things you may want to try anyway:
Enclose the whole loop with an inputList.BeginUpdate and an inputList.EndUpdate. This prevents the list to refresh each time you add an item.
Try SynchronizationContext and the Post method instead of the old fashioned Invoke. This might make the UI invocation smoother. See this answer to check how it works.
By the way, you can remove the first Invoke, if it is possible for you to move the three lines that ends with .backcolor = Color.White in the second Invoke.
If it is not better, then try to separate the process of building the items and displaying them :
Use parallelization fill a List of items to update (not with a ForEach, but with .AsParallel() against a LINQ query)
Use a loop to update your ListView.
If this is not satisfying, you will probably have to find a workaround using a ListView in virtual mode. This answer gives some hints to fill a virtual ListView an asynchronous way.
Related
I know how to change the colors of a "Row" in a DataGridView, my difficulty is that, I have to list all the processes that are running (done), but I need to put a different foreground color for each process tree and without repeting any process on the list, this became quite complicated for me as a beginner, the only thing I found was a method that made my program extremely slow here in the stackoverflow, if there is any more optimized method I would appreciate it.
Here's my old code that only get ths list of processes:
private void getProcessList ()
{
Process [] processesList = Process.GetProcesses ();
foreach (Process process in processesList)
{
double memory = float.Parse (ConvertBytesToMegabytes (process.PrivateMemorySize64) .ToString ());
memory = Math.Round (memory, 2);
string processState = "";
if (process.Responding == true)
stateProc = "Active";
else
stateProc = "Does not answer";
dataGridView1.Rows.Add (process.ProcessName, processState, process.Id, memory + "MB", process.Threads.Count, btnFinalizarProc);
}
totalProc = processesList.Length;
}
Since the definition of btnFinalizarProc is missing in your code, I have omitted the btnFinalizarProc column in the DataGridView.
You can try to use Color.FromArgb() and Random to generate random colors.
And then define a dictionary to store Name_Color key-value pairs
Here is a demo you can refer to.
Dictionary<string, Color> name_color = new Dictionary<string, Color>();
private void getProcessList()
{
Random random = new Random();
Process[] processesList = Process.GetProcesses();
foreach (Process process in processesList)
{
double memory = float.Parse(ConvertBytesToMegabytes(process.PrivateMemorySize64).ToString());
memory = Math.Round(memory, 2);
string processState = "";
if (process.Responding == true)
processState = "Active";
else
processState = "Does not answer";
// Check if contains
if (!name_color.ContainsKey(process.ProcessName))
{
int int_Red = random.Next(256);
int int_Green = random.Next(256);
int int_Blue = random.Next(256);
Color color = Color.FromArgb(int_Red, int_Green, int_Blue);
name_color.Add(process.ProcessName, color);
}
DataGridViewRow row = (DataGridViewRow)dataGridView1.Rows[0].Clone();
row.Cells[0].Value = process.ProcessName;
row.Cells[1].Value = processState;
row.Cells[2].Value = process.Id;
row.Cells[3].Value = memory + "MB";
row.Cells[4].Value = process.Threads.Count;
row.DefaultCellStyle.ForeColor = name_color[process.ProcessName];
dataGridView1.Rows.Add(row);
}
}
private void button1_Click(object sender, EventArgs e)
{
getProcessList();
}
Test result,
Is there any way to change the inlines from a BackgroundWorker?
I tried the following:
private void test()
{
var rows = GetDataGridRows(dgVarConfig);
foreach (DataGridRow r in rows)
{
TextBlock tb = cMatchEx.GetCellContent(r) as TextBlock;
if (!syntaxWorker.IsBusy)
syntaxWorker.RunWorkerAsync(new KeyValuePair<TextBlock, String>(tb, tb.Text));
}
}
private void syntaxWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (e.Argument == null)
Thread.Sleep(100);
else
{
KeyValuePair<TextBlock, String> kvp = (KeyValuePair<TextBlock, String>)e.Argument;
e.Result = new KeyValuePair<TextBlock, List<Run>>(kvp.Key, Syntax.Highlight(kvp.Value));
}
}
private void syntaxWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
KeyValuePair<TextBlock, List<Run>> kvp = (KeyValuePair<TextBlock, List<Run>>)e.Result;
TextBlock tb = kvp.Key;
tb.Text = "";
kvp.Value.ForEach(x => tb.Inlines.Add(x));
}
}
And the syntax class:
public static class Syntax
{
static Regex subFormula = new Regex(#"\w+\(\)");
static Regex sapFormula = new Regex(#"\w+\(([^)]+)\)");
static Regex strings = new Regex(#"\'[^']+\'");
static Regex numerals = new Regex(#"\b[0-9\.]+\b");
static Regex characteristic = new Regex(#"(?:)?\w+(?:)?");
static Regex andOr = new Regex(#"( and )|( AND )|( or )|( OR )");
static Regex not = new Regex(#"(not )|(NOT )");
private static Brush[] colorArray;
public static List<Run> Highlight(String input)
{
colorArray = new Brush[input.Length];
for (int i = 0; i < input.Length; i++)
colorArray[i] = Brushes.Black;
//Reihenfolge beibehalten!!
assignColor(Brushes.Blue, characteristic.Matches(input));
assignColor(Brushes.Black, andOr.Matches(input));
assignColor(Brushes.Black, numerals.Matches(input));
assignColor(Brushes.Orange, strings.Matches(input));
assignColor(Brushes.DeepPink, subFormula.Matches(input));
assignColor(Brushes.Green, sapFormula.Matches(input));
assignColor(Brushes.Green, not.Matches(input));
int index = 0;
List<Run> runList = new List<Run>();
foreach (Char character in input)
{
runList.Add(new Run(character.ToString()) { Foreground = colorArray[index] });
index++;
}
colorArray = null;
return runList;
}
public static void Check(TextBlock textBlock)
{
}
private static void assignColor(Brush brush, MatchCollection matchCollection)
{
foreach (Match match in matchCollection)
{
int start = match.Index;
int end = start + match.Length;
for (int i = start; i < end; i++)
{
colorArray[i] = brush;
}
}
}
}
I alway get this error: The calling thread cannot access this object because a different thread owns it.
I tried many different things: return the runList with progress changed, changed the static syntax class to a normal class.. but nothing worked, its always the same error.
I also tried to invoke it from the Backgroundworker.. that means call
List<Run> runList = Syntax.Highlight(kvp.Value);
this.Dispatcher.Invoke((Action)(() =>
{
runList.ForEach(x => publicRunList.Add(x));
}));
Anybody knows the problem?
Use
tb.Dispatcher.Invoke(() => {
tb.Text = "";
kvp.Value.ForEach(x => tb.Inlines.Add(x));
});
instead of
tb.Text = "";
kvp.Value.ForEach(x => tb.Inlines.Add(x));
Gui elements can only be accessed from the Gui thread. Using Dispatcher.Invoke ensures that the invoked action runs on it.
You are also creating Run objects in Syntax.Highlight. You also have to create Gui elements on the Gui thread. So you should also wrap this call in a dispatcher invoke:
e.Result = new KeyValuePair<TextBlock, List<Run>>(kvp.Key, Syntax.Highlight(kvp.Value));
This should work:
//this runs synchronously
kvp.Key.Dispatcher.Invoke(() => {
e.Result = new KeyValuePair<TextBlock, List<Run>>(kvp.Key, Syntax.Highlight(kvp.Value));
});
//this runs asynchronously
kvp.Key.Dispatcher.BeginInvoke((Action)(() => {
e.Result = new KeyValuePair<TextBlock, List<Run>>(kvp.Key, Syntax.Highlight(kvp.Value));
}));
This probably defeats the purpose of why you wanted to use a BackgroundWorker in the first place. I'd suggest to change the interface of Syntax.Highlight to return a list of tuples with the string and the highlight color instead, and then create the Run objects on the Gui thread.
Edit:
As Gopichandar noted, using BeginInvoke executes the given Action asynchronously, so that would solve the freezing of the application. It would still take a couple of seconds until all elements are added to the Gui though.
In WPF, only the thread that the UI element belongs to (i.e. the UI thread) can communicate with it. The DoWork part of the BackgroundWorker is executed in a different thread and thus cannot do anything UI-related. The same thing goes for Timers instead of BackgroundWorkers.
But if you create the BackgroundWorker with var worker = new BackgroundWorker {WorkerReportsProgress = true}; then you can set an event handler for ProgressChanged. Inside your _DoWork(), you can then say: (sender as BackgroundWorker).ReportProgress, which will call your ProgressChanged event handler in the original thread, where you can manipulate the UI elements.
Full example:
http://www.wpf-tutorial.com/misc/multi-threading-with-the-backgroundworker/
As part of my school project I'm trying to link two tables to decrease the amount of data stored in one table so I wanted to link my "Scores" class with my "CorrectAnswers" class via the ObjectID. However since the tasks are asynchronous, by the time one task is done saving, the other task has already begun or also finished saving and so the ObjectID returns as null.
Here's the code I'm using:
public void SaveScore()
{
ParseObject SendScore = new ParseObject("Scores");
SendScore["Score"] = CheckAnswer.score;
SendScore["user"] = ParseObject.CreateWithoutData("_User", ParseUser.CurrentUser.ObjectId);
SendScore["TestMode"] = MainMenu.testmode;
SendScore["TotalQuestions"] = QuestionCreation.TotalQuestions;
SendScore["CorrectQuestions"] = CheckAnswer.CorrectQuestions;
SendScore.SaveAsync().ContinueWith(t =>
{
ScoreObjectId = SendScore.ObjectId;
});
ParseObject SendCorrectTopics = new ParseObject("CorrectAnswers");
SendCorrectTopics["Score"] = SendScore.ObjectId;
for (int i = 0; i <= 9; i++)
{
string Topic = "Topic" + (i + 1).ToString();
SendCorrectTopics[Topic] = CheckAnswer.CorrectTopics[i];
}
SendCorrectTopics.SaveAsync();
SceneManager.LoadScene(0);
}
How would I be able to make the second save hold until the first save has finished? I'm somewhat new to C# and so don't quite know all it's features yet. I've looked into "await" but unity doesn't seem to like that. Any help would be greatly appreciated!
Thanks in advance,
EDIT: Okay, after a bit more reading on Unity's coroutines, I found a much better way of checking that only relies on checking when needed:
IEnumerator CheckSave()
{
while(ScoreObjectId == null & !DoneSave))
{
print("Running");
yield return new WaitForSeconds(0.5f);
}
DoneSave = false;
SaveTotalTopics();
}
This seems like a much better way of doing it.
Well, it seems the answer was something I've already done before, even if it is a little ugly.
Using Unity's update function I created a check to make sure the ObjectID is not null and the previous save had completed, as so:
void Update () {
if (ScoreObjectId != null & DoneSave)
{
DoneSave = false;
SaveTotalTopics();
}
Thus splitting it into two saves and creating:
public void SaveScore()
{
ParseObject SendScore = new ParseObject("Scores");
SendScore["Score"] = CheckAnswer.score;
SendScore["user"] = ParseObject.CreateWithoutData("_User", ParseUser.CurrentUser.ObjectId);
SendScore["TestMode"] = MainMenu.testmode;
SendScore["TotalQuestions"] = QuestionCreation.TotalQuestions;
SendScore["CorrectQuestions"] = CheckAnswer.CorrectQuestions;
Task SendingScores = SendScore.SaveAsync().ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
DoneSave = false;
print(t.Exception);
}
else
{
DoneSave = true;
print("Setting object ID!");
ScoreObjectId = SendScore.ObjectId;
print(ScoreObjectId);
}
});
}
void SaveTotalTopics()
{
for (int i = 0; i <= 9; i++)
{
string Topic = "Topic" + (i + 1).ToString();
SendCorrectTopics[Topic] = CheckAnswer.CorrectTopics[i];
}
SendCorrectTopics["UserScore"] = ParseObject.CreateWithoutData("Scores", ScoreObjectId);
SendCorrectTopics.SaveAsync().ContinueWith(t =>
{
if(t.IsFaulted || t.IsCanceled)
{
print(t.Exception);
}
else
{
print("Saved!");
}
});
}
I'd also forgotten to use ParseObject.CreateWithoutData() so my first code snippet wouldn't have worked even if I'd found a better method...
So, although I'm not happy with the final result, at least it works and I don't think running an if statement every frame should significantly impact on my game's performance.
Why not use a bool and a while loop?
public IEnumerator SaveScore()
{
bool canContinue = false;
ParseObject SendScore = new ParseObject("Scores");
SendScore["Score"] = CheckAnswer.score;
SendScore["user"] = ParseObject.CreateWithoutData("_User", ParseUser.CurrentUser.ObjectId);
SendScore["TestMode"] = MainMenu.testmode;
SendScore["TotalQuestions"] = QuestionCreation.TotalQuestions;
SendScore["CorrectQuestions"] = CheckAnswer.CorrectQuestions;
SendScore.SaveAsync().ContinueWith(t =>
{
ScoreObjectId = SendScore.ObjectId;
//set the bool canContinue to true because the first portion of code has finished running
canContinue = true;
});
//wait while the canContinue bool is false
while(!canContinue){
yield return null;
}
//continue your parse code
ParseObject SendCorrectTopics = new ParseObject("CorrectAnswers");
SendCorrectTopics["Score"] = SendScore.ObjectId;
for (int i = 0; i <= 9; i++)
{
string Topic = "Topic" + (i + 1).ToString();
SendCorrectTopics[Topic] = CheckAnswer.CorrectTopics[i];
}
SendCorrectTopics.SaveAsync();
SceneManager.LoadScene(0);
return null;
}
When I want to clear the following array:
userInfo[0] = txtPassword.Text;
userInfo[1] = txtVoornaam.Text;
userInfo[2] = txtAchternaam.Text;
userInfo[3] = txtWoonplaats.Text;
userInfo[4] = txtPostcode.Text;
userInfo[5] = txtTelnr.Text;
I'm doing the following:
for (int i = 0; i < 5; i++)
{
userInfo[i] = "";
}
That obviously doesn't work and when I try this:
foreach (string item in userInfo)
{
item = "";
}
Also does not work. How can I do this without saying:
userinfo[0] = txtPassword;
If you want to treat them array-like, you need to add them to an array, or rather and much better a List<TextBox>. Do it once and you can loop over it for any or your needs..
Do it once..:
List<TextBox> boxes = new List<TextBox>()
{ txtPassword, txtVoornaam, txtAchternaam, txtWoonplaats, txtPostcode, txtTelnr};
Now you can pull out the values into a string array
string[] myTexts = boxes.Select(x => x.Text).ToArray();
..or a List :
List<string> myTexts = boxes.Select(x => x.Text).ToList();
or you can clear them all:
foreach (TextBox tb in boxes) tb.Text = "";
If you need to you can access the List elements by variable:
boxes.Find(x => x == textBox2).Text = "12345";
..or by Name:
boxes.Find(x => x.Name == "txtVoornaam").Text = "Jens";
(All checks omitted..)
Question
How do I get the cpu usage of each process into PopulateApplications()?
What's happening
getCPUUsage() gives me the same value for each process. It's like it's getting the cpu usage for only one process.
The rest of the code seems to work fine.
getCPUUsage() from class Core:
public static double getCPUUsage()
{
ManagementObject processor = new ManagementObject("Win32_PerfFormattedData_PerfOS_Processor.Name='_Total'");
processor.Get();
return double.Parse(processor.Properties["PercentProcessorTime"].Value.ToString());
}
What I've tried
In form1, I have a method by which I display information about processes like icons, name, and statuses (i.e. running/not running).
void PopulateApplications()
{
DoubleBufferedd(dataGridView1, true);
int rcount = dataGridView1.Rows.Count;
int rcurIndex = 0;
foreach (Process p in Process.GetProcesses())
{
try
{
if (File.Exists(p.MainModule.FileName))
{
var icon = Icon.ExtractAssociatedIcon(p.MainModule.FileName);
Image ima = icon.ToBitmap();
ima = resizeImage(ima, new Size(25, 25));
ima = (Image)(new Bitmap(ima, new Size(25, 25)));
String status = p.Responding ? "Running" : "Not Responding";
if (rcurIndex < rcount - 1)
{
var currentRow = dataGridView1.Rows[rcurIndex];
currentRow.Cells[0].Value = ima;
currentRow.Cells[1].Value = p.ProcessName;
currentRow.Cells[2].Value = cpuusage;
currentRow.Cells[3].Value = status;
}
else
{
dataGridView1.Rows.Add(
ima, p.ProcessName,cpuusage, status);//false, ima, p.ProcessName, status);
}
rcurIndex++;
}
}
catch ( Exception e)
{
string t = "error";
}
}
if (rcurIndex < rcount - 1)
{
for (int i = rcurIndex; i < rcount - 1; i++)
{
dataGridView1.Rows.RemoveAt(rcurIndex);
}
}
}
I added this line:
currentRow.Cells[2].Value = cpuusage;
cpuusage is double-type variable.
I changed this line, also, to include addition of cpuusage:
dataGridView1.Rows.Add(
ima, p.ProcessName,cpuusage, status);
Now I have a background worker event, dowork, whereby I use cpuusage to get the cpu usage values:
this.Invoke(new Action(() => cpuusage = Core.getCPUUsage()));
Maybe I don't need to call the method getCPUUsage() through backgroundworker.
This is what i see when im running the program:
All the processes have the same cpu usage ? Not logic.
Then when there is an update i see:
Again all the cells have the same cpu usage value. But on the left there are many processes each should have it's own cpu usage.